For Developers
Calling

Calls

Have an agent call a phone number, track the call by its ID, and read why it failed if it did.

Use this resource to place one outbound call from one of your agents. A successful request returns a callId straight away, and you read the call's progress with that ID. A 201 means the call was accepted, not that the phone rang. To place calls in bulk, use campaigns.

EndpointRequest
Place a callPOST/v1/calls
Get a callGET/v1/calls/{id}

The call object

GET /v1/calls/{id} returns this object. Fields are in snake_case.

Attributes

idstringrequired
The call's UUID. This is the callId from POST /v1/calls.
statusstringrequired
queued, dialing, in_progress, completed or failed. See Statuses.
failure_codestring | nullrequired
Why the call failed. null unless status is failed. See Failure codes.
failure_messagestring | nullrequired
A readable version of failure_code.
agent_idstring | nullrequired
The agent that made the call.
phone_number_idstring | nullrequired
The phone number the call was made from.
provider_call_idstring | nullrequired
The voice provider's ID for this call. null until the call is sent out. Quote it when you ask about a call.
contact_phonestringrequired
The number that was called, in E.164 format.
contact_dataobject | nullrequired
The lead details sent with the call. Keys are normalized to snake_case, and empty values are removed. See Custom fields.
lead_notesstring | nullrequired
The leadNotes value sent with the call.
duration_secondsinteger | nullrequired
Call length in seconds, once known.
cost_usdnumber | nullrequired
Call cost in US dollars, once known.
created_atstringrequired
When the call was requested, as an ISO 8601 timestamp.
completed_atstring | nullrequired
When the call was marked completed or failed.

Statuses

StatusMeaning
queuedAccepted and waiting. It may be waiting for calling hours or a free line. No funds are held yet.
dialingFunds are held, and the call is about to go out.
in_progressThe call has been sent to the voice provider. This doesn't mean anyone has answered.
completedThe call ended normally.
failedThe call won't be retried. failure_code says why.

completed and failed are final.

Failure codes

A failed call has one of these values in failure_code.

CodeMeaningWhat to do
account_lockedThe account is locked.Contact support.
agent_inactiveThe agent became inactive while the call waited.Activate the agent, then place a new call.
agent_not_foundThe agent was deleted while the call waited.Use another agent.
phone_number_not_foundThe phone number was removed while the call waited.Use another number.
insufficient_balanceThe wallet couldn't cover the hold for the call's maximum duration.Add funds, then place a new call.
unresolved_placeholdersThe first message has placeholders with no value in this request.Send values for them, or change the message.
line_capacity_reachedAll your lines stayed busy for the 5 minutes this call could wait.Place a new call when lines are free.
system_busyThe platform stayed at capacity for those 5 minutes.Try again shortly.
outside_calling_hoursThe recipient's calling hours didn't open within 35 hours.Check the number and any timezone you sent.
call_not_readyThe agent or phone number stopped being ready while the call waited.Check them, then place a new call.
too_many_queuedToo many calls were already queued or in progress.Wait for some to finish.
queue_unavailableThe call couldn't be queued.Place a new call with a new key.
call_not_startedThe call couldn't be started.Place a new call. If this continues, contact support.
call_failedThe call went out, then ended with no answer, a busy line, a rejection or an error.Decide whether to call again.

To try a failed call again, send a new Idempotency-Key.

Place a call

POST/v1/callswrite key

Accepts an outbound call and returns 201 with a callId. This is a paid operation. Charges come from your wallet.

Headers

Idempotency-Keystringrequired
A key you choose for this call, of 1 to 255 letters, numbers, dashes or underscores. See Idempotency.

Body parameters

agentIdstringrequired
UUID of the agent that will make the call.
phoneNumberIdstringrequired
UUID of the phone number to call from. It must be assigned to agentId.
phoneNumberstringrequired
The number to call. See Phone number formats.
firstNamestring
The lead's first name, up to 200 characters. Overrides a matching key in customFields.
lastNamestring
The lead's last name, up to 200 characters. Overrides a matching key in customFields.
emailstring
The lead's email address. Must be a valid email. Overrides a matching key in customFields.
companystring
The lead's company, up to 200 characters. Overrides a matching key in customFields.
customFieldsobject
Any other lead details, as key and value pairs. The agent receives them as variables. See Custom fields.
firstMessagestring
What the agent says first, up to 1,000 characters. Replaces the agent's configured intro for this call. See First message.
callContextstringdefault outbound
outbound or callback. Selects which of the agent's configured intros to use. It has no effect when you send firstMessage.
voiceCloneIdstring
UUID of a voice to use instead of the agent's voice. If the ID doesn't match a usable voice on your account, the call uses the agent's voice and returns no error.
leadNotesstring
Free-text notes saved on the call, up to 5,000 characters. They are not passed to the agent during the call.

Fields that aren't listed here are ignored.

Response data

callIdstringrequired
The call's UUID. Pass it to GET /v1/calls/{id}.
statusstringrequired
queued on a new call.
jobIdstring
A reference for the queued work. You don't need it to track the call, and no route accepts it.
failure_codestring
Only on a repeat of a call that already failed. See Idempotency.
failure_messagestring
A readable version of failure_code.

Idempotency

Every request needs an Idempotency-Key header, so a retry after a timeout can't place a second call. Use one key per call you intend to make. A UUID or your own ID for the lead works well.

You sendResult
A new keyThe call is checked and, if it passes, accepted with 201.
The same key and same body, within 24 hoursThe original response, with its original status and an Idempotency-Replay: true header. No second call.
The same key and same body, after 24 hours200 with the call as it stands, and an Idempotency-Replay: true header. It includes failure_code and failure_message if the call failed. No second call.
The same key with a different body409 IDEMPOTENCY_KEY_REUSED. Nothing is placed.
The same key while the first request is still running409 REQUEST_IN_PROGRESS. Wait, then retry with the same key.
No key400 MISSING_IDEMPOTENCY_KEY.
A key in the wrong format400 INVALID_IDEMPOTENCY_KEY.

Bodies are compared by their content, so the order of JSON keys doesn't matter.

If the call was lost before it started, a repeat with the same key and body queues it again and returns 201. It still can't be dialed twice.

A replay returns what happened at the time, so status can be out of date. Read GET /v1/calls/{id} for the current state.

The body is validated before the key is looked at, so fixing an invalid body doesn't need a new key. When the request is refused with a 403, 404, 422 or 429, the key is released. Retry with the same key once you've cleared the cause.

A 503 IDEMPOTENCY_UNAVAILABLE means nothing was placed. Retry with the same key shortly.

A 503 SERVICE_UNAVAILABLE with Idempotency-Replay: true means the call couldn't be queued on the first attempt. That same key will keep returning this answer for 24 hours, so send a new key.

Phone number formats

phoneNumber is converted to E.164 before the call is queued. A number is accepted in these forms:

InputTreated as
+12025550123 or +1 (202) 555-0123International number. Spaces and punctuation are removed.
(202) 555-0123 or 202555012310 digits, so a US or Canadian number with +1 added.
1202555012311 digits starting with 1, so + is added.
442079460123More than 10 digits not starting with 1, so + is added.

The result must be a valid number for its country. Anything else returns 400. Send the full international form for numbers outside the US and Canada.

Custom fields

customFields can have up to 50 keys, each up to 100 characters. It can be up to 16 KB in total, nested up to 32 levels deep.

Keys are converted to snake_case, so orderId becomes order_id. Common variants become one standard key, for example firstname and first both become first_name. Keys with empty or null values are dropped. A full_name value is built from the first and last name when you don't send one.

Keys that look like internal identifiers, such as user_id and file_id, are left out when contact_data is returned.

To narrow the recipient's calling hours, send an IANA time zone name such as America/Chicago. Use any of these keys: timezone, time_zone, timeZone, tz, customer_timezone or customerTimezone. See Calling hours.

First message

firstMessage can use placeholders in the form {{first_name}} or [first_name]. They are filled from the lead details in the request.

Calling hours

Calls only go out from 09:00 to 20:00 at the destination.

  • A call requested outside those hours still returns 201. Its status stays queued until the hours open.
  • A waiting call holds no funds and no line.
  • A call that hasn't started 35 hours after you requested it fails with outside_calling_hours. It never goes out late.

The destination's hours come from the number being called. For US and Canadian numbers, that is the area code. Otherwise it is the country. When a number covers several time zones, the call waits until all of them are open. If those zones share less than 4 hours of the day, the country's main zone applies. If no time zone can be worked out, the call goes out without this check.

A time zone you send in customFields can only narrow these hours. It never widens them.

  • A zone in the destination's country must be open as well as the number's own zones.
  • A zone in another country is ignored, and so are UTC and Etc/UTC.
  • A zone that would leave less than 4 shared hours is ignored.

Capacity

Two limits apply, and they work differently.

Queued and in-progress calls. Each account can have up to 100 calls queued or in progress through this route. Over that, the request returns 429 EXECUTION_CAPACITY_EXCEEDED with Retry-After: 60, and nothing is queued.

Concurrent lines. Each account has a limit on how many calls can be live at once. An accepted call that can't get a line stays queued and tries again every 30 seconds for up to 5 minutes. After that, it fails with line_capacity_reached, or with system_busy if the platform itself is at capacity. For a call that waited overnight, those 5 minutes start when the calling hours open.

Errors returned by this request

StatusCodeWhen
400VALIDATION_ERRORThe body failed validation. details.issues names each field.
400MISSING_IDEMPOTENCY_KEY, INVALID_IDEMPOTENCY_KEYThe key is missing or in the wrong format.
401UNAUTHORIZEDThe API key is missing or invalid.
403FORBIDDENThe API key doesn't have write permission, or its owner's role can't make changes.
403ACCOUNT_LOCKEDThe account is locked.
404NOT_FOUNDagentId or phoneNumberId doesn't exist on your account.
409IDEMPOTENCY_KEY_REUSED, REQUEST_IN_PROGRESS, DUPLICATE_REQUESTSee Idempotency.
413PAYLOAD_TOO_LARGEThe body is over 256 KB.
422AGENT_INACTIVEThe agent isn't active.
422See belowThe phone number, agent or destination can't be used for this call.
429RATE_LIMIT_EXCEEDEDThe API key is over its rate limit.
429EXECUTION_CAPACITY_EXCEEDEDYou have 100 calls queued or in progress. Retry after 60 seconds.
503IDEMPOTENCY_UNAVAILABLE, SERVICE_UNAVAILABLESee Idempotency.

These 422 codes all include details.action, which says how to fix the problem.

CodeMeaning
AGENT_NOT_READYThe agent isn't set up to place calls.
PHONE_NUMBER_AGENT_MISMATCHThe phone number is assigned to a different agent.
PHONE_NUMBER_UNASSIGNEDThe phone number isn't assigned to an agent.
PHONE_NUMBER_INACTIVEThe phone number isn't active.
PHONE_NUMBER_OUTBOUND_UNSUPPORTEDThe phone number can't make outbound voice calls. For example, Australian 1800 numbers are receive-only.
PHONE_NUMBER_COMPLIANCE_PENDINGThe phone number's regulatoryStatus is pending.
PHONE_NUMBER_COMPLIANCE_BLOCKEDThe phone number's regulatoryStatus is blocked.
PHONE_NUMBER_NOT_SYNCEDThe phone number is still being set up, or needs setting up again.
PHONE_NUMBER_NOT_READYThe phone number's setup isn't finished.
DESTINATION_NOT_ALLOWEDThis phone number isn't allowed to call that destination.
DESTINATION_NOT_READYYou are calling one of your own numbers, and it isn't ready to receive a call.
CALL_NOT_READYThe call can't be placed right now, for another reason.

Check isCallableForCampaigns on the phone number to catch most of these before you send a request.

Checks after the request succeeds

The checks above run again while the call waits, because things can change. Funds are checked only at this stage. A hold is placed on your wallet before dialing, covering the agent's maximum call duration, rounded up to whole minutes, at the per-minute price.

If one of these checks fails, the call's status becomes failed with a failure code. Read it with GET /v1/calls/{id}.

Request
curl -X POST https://backend.synthesys.app/api/v1/calls \
  -H "SYNTH_API_KEY: YOUR_API_KEY" \
  -H "Idempotency-Key: lead-1042-renewal-call" \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "6f1d2a34-8b7c-4e21-9a10-3c5d7e9f1b20",
    "phoneNumberId": "b2c4d6e8-1a3f-4c5d-8e7f-90a1b2c3d4e5",
    "phoneNumber": "+12025550123",
    "firstName": "Jane",
    "email": "jane@example.com",
    "customFields": { "timezone": "America/Chicago", "order_id": "A-1042" },
    "firstMessage": "Hi {{first_name}}, I am calling about order {{order_id}}.",
    "callContext": "outbound",
    "leadNotes": "Asked for a call back after 3pm."
  }'
Response (201)
{
  "success": true,
  "data": {
    "callId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
    "jobId": "0c9e7a52-4d1b-4f36-a8e2-7b6c5d4e3f21",
    "status": "queued"
  }
}
Phone number or agent error (422)
{
  "success": false,
  "error": "This phone number is assigned to a different agent.",
  "code": "PHONE_NUMBER_AGENT_MISMATCH",
  "requestId": "e3a1c5d7-2b4f-4a6c-9d8e-1f2a3b4c5d6e",
  "timestamp": "2026-10-31T14:02:11.482Z",
  "details": {
    "action": "Use the agent assigned to this number, or reassign the number first."
  }
}
Key reused with a different body (409)
{
  "success": false,
  "error": "This Idempotency-Key was already used with a different request. Send a new key to make a new request.",
  "code": "IDEMPOTENCY_KEY_REUSED",
  "requestId": "1d2c3b4a-5f6e-4d7c-8b9a-0f1e2d3c4b5a",
  "timestamp": "2026-10-31T14:03:40.120Z"
}

Get a call

GET/v1/calls/{id}read key

Returns one call object. Use the callId from POST /v1/calls. It works from the moment the call is accepted.

Path parameters

idstringrequired
The call's UUID.

Errors

StatusWhen
400The ID isn't a valid UUID.
404No call with this ID exists on your account. error is Call not found.
Request
curl https://backend.synthesys.app/api/v1/calls/9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d \
  -H "SYNTH_API_KEY: YOUR_API_KEY"
Response
{
  "success": true,
  "data": {
    "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
    "status": "completed",
    "failure_code": null,
    "failure_message": null,
    "agent_id": "6f1d2a34-8b7c-4e21-9a10-3c5d7e9f1b20",
    "phone_number_id": "b2c4d6e8-1a3f-4c5d-8e7f-90a1b2c3d4e5",
    "provider_call_id": "2e4f6a8c-1b3d-4f5a-9c7e-0d2b4f6a8c1e",
    "contact_phone": "+12025550123",
    "contact_data": {
      "first_name": "Jane",
      "full_name": "Jane",
      "email": "jane@example.com",
      "timezone": "America/Chicago",
      "order_id": "A-1042"
    },
    "lead_notes": "Asked for a call back after 3pm.",
    "duration_seconds": 184,
    "cost_usd": 1.84,
    "created_at": "2026-10-31T14:02:12.103Z",
    "completed_at": "2026-10-31T14:05:20.511Z"
  }
}
Failed call
{
  "success": true,
  "data": {
    "id": "7c6d5e4f-3a2b-4c1d-8e0f-9a8b7c6d5e4f",
    "status": "failed",
    "failure_code": "insufficient_balance",
    "failure_message": "The wallet balance is too low to place this call.",
    "agent_id": "6f1d2a34-8b7c-4e21-9a10-3c5d7e9f1b20",
    "phone_number_id": "b2c4d6e8-1a3f-4c5d-8e7f-90a1b2c3d4e5",
    "provider_call_id": null,
    "contact_phone": "+12025550123",
    "contact_data": { "first_name": "Jane", "full_name": "Jane" },
    "lead_notes": null,
    "duration_seconds": null,
    "cost_usd": null,
    "created_at": "2026-10-31T14:02:12.103Z",
    "completed_at": "2026-10-31T14:02:13.871Z"
  }
}