Skip to content

Place Calls ​

Each kind of call has its own endpoint. Pick the one that matches what should happen when the callee answers:

KindRouteWhen the callee answersReply
DialPOST /telephony/call/dialNothing plays — use the call to park or transfer it.202 queued
TTSPOST /telephony/call/ttsA message is spoken, then the call hangs up.202 queued
AgentPOST /telephony/call/agentAn AI agent talks with them.201 ringing
SurveyPOST /telephony/call/surveyQuestions are asked and the answers returned.202 or 201
FlowPOST /telephony/call/flowNot available yet — the call rings, but no flow runs.201 ringing
BatchPOST /telephony/call/batchOne TTS or dial call per number.202 queued

Then track any of them with GET /telephony/call/{call_uuid}.

Your API key needs the voice.originate permission. A call to an internal extension (POST /telephony/call/extension) needs a signed-in user instead — an API key has no handset to ring.

Each endpoint accepts only its own fields

A field that belongs to another kind of call is refused with 400 and names the right endpoint — for example survey belongs to POST /telephony/call/survey. The older POST /telephony/call still works for dial, TTS, agent, flow and extension calls, but is deprecated and cannot run a survey.

Who is called, and from where ​

Every call to a number takes the same target fields:

FieldTypeNotes
destinationstringRequired. The number to call, in E.164 (+66812345678).
caller_idstringOptional. One of your numbers to show the callee.
caller_identity_idstringOptional. A saved caller identity instead of caller_id.
caller_extension_uuidstringOptional. Call as this extension's number.
trunkstringOptional. Force a specific trunk.
organization_idstringOnly with a user token. An API key already names its organization.

Dial, TTS and batch also take mode: "queue" (default) waits for a free line; "instant" dials now or answers 429 when every line is busy.

Retry safely with Idempotency-Key ​

Send an Idempotency-Key header on every call you place. A retry with the same key returns the first call instead of dialing the callee a second time. The official SDKs send one for you and reuse it on their own retries.

bash
curl -X POST "$ME_BASE/telephony/call/tts" \
  -H "API-Key: $ME_API_KEY" \
  -H "X-TenantID: $ME_TENANT" \
  -H "Idempotency-Key: order-1042-reminder" \
  -H "Content-Type: application/json" \
  -d '{
    "destination": "+66812345678",
    "tts": { "text": "Your order 1042 is out for delivery.", "voice_id": "eve" }
  }'
json
{
  "kind": "pstn",
  "call": {
    "call_uuid": "5d2c…",
    "destination": "+66812345678",
    "status": "queued",
    "tts": { "provider": "xai", "voice_id": "eve", "characters": 36, "repeat": 0, "cached": false }
  }
}

call.tts reports how the message was rendered.

tts takes text, voice_id, and optionally provider, language and repeat (how many times it plays; 0 means once).

Connect an AI agent ​

json
{
  "destination": "+66812345678",
  "agent": { "id": "agent_support_th" },
  "gather": {
    "parameters": {
      "type": "object",
      "properties": { "order_id": { "type": "string" } },
      "required": ["order_id"]
    },
    "callback_url": "https://example.com/hooks/order"
  }
}

agent.id names a saved agent (add config_mode: "draft" to use its draft). system_prompt, voice and language override that agent for this call only. gather is optional: the agent asks for the fields in parameters and returns them — see Getting the answers.

Run a survey ​

A survey calls the number, asks your questions, and returns the answers. It runs in one of two modes.

Keypad ("mode": "dtmf") ​

A voice reads each question and the callee answers by pressing one key.

json
{
  "destination": "+66812345678",
  "survey": {
    "mode": "dtmf",
    "survey_id": "csat_q3",
    "greeting": "Thanks for calling us today.",
    "questions": [
      { "key": "score", "prompt": "How satisfied were you? Press 1 to 5.", "min": 1, "max": 5 },
      { "key": "would_return", "prompt": "Would you use us again? Press 1 for no, 2 for yes.", "min": 1, "max": 2, "required": false }
    ],
    "retry_prompt": "Please press a number from {min} to {max}.",
    "closing": "Thank you. Goodbye.",
    "tts": { "provider": "xai", "voice_id": "eve", "language": "en" },
    "callback_url": "https://example.com/hooks/survey"
  }
}
FieldNotes
questionsRequired. Up to 20, asked in order. Each takes one key between min and max (0–9).
keyLowercase letters, digits and _, starting with a letter. Names the answer.
requiredDefault true. An unanswered required question ends the call; an optional one is skipped.
retry_promptSaid before a question is asked again after a key outside its range. {min} and {max} are filled in. Without it, the question is simply repeated. The callee gets 3 tries.
survey_idGroups the answers in your survey reports. Each answer is stored as <survey_id>.<key> — csat_q3.score — so every question keeps its own scale. Default telephony_survey.
ttsThe voice for every prompt: provider, voice_id, language, instructions. An unavailable provider is refused with 400 before the call is queued.

A keypad survey is queued: the reply is 202 with status: "queued".

Put the better answer on the higher key

Survey reports count a score as satisfied when it is in the top two of its scale. For a yes/no question, make yes the higher key (1 no, 2 yes) so the report reads the way you expect.

Spoken ("mode": "agent", the default) ​

An AI agent asks for the fields in parameters by voice and hangs up once every required answer is valid. It rings now: the reply is 201.

json
{
  "destination": "+66812345678",
  "survey": {
    "greeting": "Hi, this is a short survey from Acme.",
    "parameters": {
      "type": "object",
      "properties": {
        "rating": { "type": "integer", "x-parse": "integer" },
        "comment": { "type": "string" }
      },
      "required": ["rating"]
    },
    "language": "th",
    "callback_url": "https://example.com/hooks/survey"
  }
}

x-parse sets the format the agent returns for a field: text, integer, number, boolean, date, phone or email. Keypad-only fields (questions, closing, retry_prompt, survey_id, tts) are refused here.

Getting the answers ​

Answers from a gather or a survey reach you two ways, with the same body:

  • Webhook — the call.ai_gather.ended event on your workspace webhooks, sent when an answered call ends, with status valid or incomplete.
  • callback_url — only when every required answer is valid. It is signed: X-webhook-signature is the base64 HMAC-SHA256 of the raw body, keyed with call.gather.callback_secret from the place reply (or the callback_secret you sent).
json
{
  "event": "call.ai_gather.ended",
  "id": "evt_…",
  "created_at": "2026-09-25T09:30:12Z",
  "workspace_id": "org_…",
  "gather_id": "…",
  "session_uuid": "…",
  "status": "valid",
  "result": { "score": 4, "would_return": 2 }
}

Match gather_id against call.gather.gather_id from the place reply.

Call many numbers ​

json
{
  "destinations": ["+66811111111", "+66822222222"],
  "tts": { "text": "The office is closed today.", "voice_id": "eve" }
}
json
{ "queued": 2, "total": 2, "calls": [{ "destination": "+66811111111", "call": { "call_uuid": "…", "status": "queued" } }] }

A number that could not be queued has an error in its item; the rest still go out.

Call status ​

bash
curl "$ME_BASE/telephony/call/$CALL_UUID" \
  -H "API-Key: $ME_API_KEY" -H "X-TenantID: $ME_TENANT"
json
{ "call_uuid": "5d2c…", "room": "…", "destination": "+66812345678", "status": "completed", "handled_by_ai": false, "duration_secs": 42 }

status moves through pending, ringing and in_progress, and ends as completed, no_answer, failed or canceled.

SDKs ​

go
res, err := c.Telephony.Survey(ctx, &sdk.SurveyCallRequest{
	DialTarget: sdk.DialTarget{Destination: "+66812345678"},
	Survey: &sdk.Survey{
		Mode:      sdk.SurveyModeDTMF,
		SurveyID:  "csat_q3",
		Questions: []sdk.SurveyQuestion{{Key: "score", Prompt: "Press 1 to 5.", Min: 1, Max: 5}},
	},
})
ts
const res = await me.telephony.survey({
  destination: "+66812345678",
  survey: { mode: "dtmf", survey_id: "csat_q3", questions: [{ key: "score", prompt: "Press 1 to 5.", min: 1, max: 5 }] },
});

Every SDK has the same telephony service: dial, tts, agent, survey, flow, extension, batch and status.