Place your first call
Choose an agent and phone number, send a lead's details, place an outbound call, and track it by its ID.
You need three things before you start:
- An API key with
writepermission. See Authentication. - An active agent with a phone number assigned to it. You set both up in the dashboard, because these API routes are read-only.
- Funds in your wallet. Calls are paid, so check your balance first.
The JavaScript samples use top-level await. Save them as .mjs files and run them with Node 18 or later.
- Choose an agent
List your agents and copy the
idof one whosestatusisactive. The full object is in the agents reference.curl "https://backend.synthesys.app/api/v1/agents?limit=10" \ -H "SYNTH_API_KEY: YOUR_API_KEY" - Choose a phone number
The number must be assigned to the agent you chose. Look for a number where
assignedAgentIdmatches the agent andisCallableForCampaignsistrue. If that field isfalse,campaignExecutionMessagesays why. The full object is in the phone numbers reference.curl "https://backend.synthesys.app/api/v1/phone-numbers" \ -H "SYNTH_API_KEY: YOUR_API_KEY" - Place the call
Send the agent, the number to call from, and the lead's number and details to
POST /v1/calls. TheIdempotency-Keyheader is required. Use one key for each call you intend to make, such as your own ID for the lead.Replace
+12025550123with a real number you have permission to call.curl -X POST "https://backend.synthesys.app/api/v1/calls" \ -H "SYNTH_API_KEY: YOUR_API_KEY" \ -H "Idempotency-Key: lead-1042-growth-plan" \ -H "Content-Type: application/json" \ -d '{ "agentId": "3f0c1a52-7b7e-4c7a-9a55-1f2d3e4c5b6a", "phoneNumberId": "8d2e6b14-90c3-4f1e-b7a6-2c4d5e6f7a8b", "phoneNumber": "+12025550123", "firstName": "Jane", "lastName": "Doe", "email": "jane@example.com", "company": "Example Co", "customFields": { "planTier": "growth", "timezone": "America/New_York" }, "firstMessage": "Hi {{first_name}}, I am calling about your {{plan_tier}} plan." }'A
201means the call was accepted. It doesn't mean the call has been dialed. Keep thecallId.{ "success": true, "data": { "callId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", "jobId": "c7a1e0f4-52d8-4b36-8e19-6f0a3b7d2c11", "status": "queued" } } - Track the call
Pass the
callIdtoGET /v1/calls/{id}. It works straight away, even while the call is waiting.curl "https://backend.synthesys.app/api/v1/calls/9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d" \ -H "SYNTH_API_KEY: YOUR_API_KEY"The status moves from
queuedtodialing, thenin_progress, and ends ascompletedorfailed. A failed call has afailure_codethat says why. See Get transcripts and recordings for the next steps.
Retry safely
Network failures happen. If you don't get a response, send the same request again with the same Idempotency-Key.
| Case | What happens |
|---|---|
| Same key and same body | You get the original answer, with Idempotency-Replay: true. No second call is placed. |
| Same key and a different body | 409 IDEMPOTENCY_KEY_REUSED. Nothing is placed. |
| The first request is still running | 409 REQUEST_IN_PROGRESS. Wait, then retry with the same key. |
| The call failed and you want to try again | Send a new key. |
If a request was refused with a 403, 404, 422 or 429, nothing was placed. Fix the cause and retry with the same key. See Idempotency for the full rules.
Pass lead details
Request body
agentIdstringrequiredphoneNumberIdstringrequiredphoneNumberstringrequired+12025550123. It must be a valid number.firstNamestringlastNamestringemailstringcompanystringcustomFieldsobjectfirstMessagestringcallContextstringoutbound or callback. Selects which of the agent's configured opening lines to use.voiceCloneIdstringleadNotesstringThe name, email, company and custom fields become variables for the call. Keys are converted to snake case, so planTier becomes plan_tier. A full_name variable is built from the first and last names.
Use the variables in firstMessage as {{plan_tier}} or [plan_tier].
When the call is dialed
Calls only go out from 09:00 to 20:00 at the destination. A call requested outside those hours still returns 201, stays queued and holds no funds. It goes out when the hours open.
If a call hasn't started 35 hours after you requested it, it fails with outside_calling_hours.
The destination's hours come from the number's area code in the US and Canada, or its country elsewhere. A time zone you send can only narrow them. Use a timezone, time_zone, timeZone, tz, customer_timezone or customerTimezone key in customFields. A zone from another country is ignored, and so are UTC and Etc/UTC. See Calling hours for the details.
These hours are a platform rule. They aren't legal advice, and other rules can apply to your calls.
Your account also has a limit on calls that can be live at once. A call that can't get a line waits for up to 5 minutes, then fails with line_capacity_reached.
When a call fails
These errors come back from the request itself, and no call is placed. See Errors for the response format.
| Status | Code | Cause |
|---|---|---|
400 | VALIDATION_ERROR | A field failed validation, or phoneNumber isn't a dialable number. |
400 | MISSING_IDEMPOTENCY_KEY | The Idempotency-Key header is missing. |
403 | FORBIDDEN | The API key doesn't have write permission. |
403 | ACCOUNT_LOCKED | The account is locked. |
404 | NOT_FOUND | The agent or phone number doesn't exist in your account. |
422 | AGENT_INACTIVE | The agent isn't active. |
422 | Various | The number can't place this call. For example, it's inactive, awaiting regulatory approval or assigned to a different agent. Read details.action. |
429 | EXECUTION_CAPACITY_EXCEEDED | You have 100 calls queued or in progress. Retry after 60 seconds. |
Other checks run after the 201, including whether your wallet can fund the call. If one fails, the call's status becomes failed with a failure_code. See Failure codes.