Errors
The one error format every /v1 route uses, every stable code, and how to handle each in your integration.
Every failed /v1 request returns a non-2xx status and the same JSON envelope. Branch on code, and show or log error.
The error envelope
{
"success": false,
"error": "The request is invalid.",
"code": "VALIDATION_ERROR",
"requestId": "6f1a2b3c-4d5e-4f60-8a7b-9c0d1e2f3a4b",
"timestamp": "2026-10-31T09:30:00.000Z",
"details": {
"issues": [
{ "path": "phoneNumber", "message": "This field is required." }
]
}
}Fields
successbooleanrequiredfalse on an error.errorstringrequiredcodestringrequiredrequestIdstringrequiredtimestampstringrequireddetailsobjectdetails
issuesobject[]path and a message.actionstringbalancenumberINSUFFICIENT_BALANCE.requiredBalancenumberINSUFFICIENT_BALANCE.shortfallnumberINSUFFICIENT_BALANCE.path names the field with dots, such as customFields or conditions.conditions.0.field. It is an empty string when the problem is with the whole body or query.
Every /v1 response has Cache-Control: no-store, because it holds one account's data.
Status codes
| Status | Meaning | What to do |
|---|---|---|
200 | Success. | |
201 | Created, or a call was accepted. | |
400 | The request is invalid. | Fix the request. Don't retry it unchanged. |
401 | The API key is missing or isn't valid. | Check the SYNTH_API_KEY header. |
402 | The wallet can't pay for this. | Add funds. |
403 | The key or its owner isn't allowed to do this. | Check the key's permissions and the owner's role. |
404 | The resource or route doesn't exist, or belongs to another account. | Check the ID and path. |
409 | The request conflicts with the resource's current state. | See the code. |
413 | The body is too large. | Send less data. See Body limits. |
415 | The Content-Type isn't supported. | Send application/json. |
422 | The request is valid, but the resources aren't ready for it. | Follow details.action. |
429 | You hit a limit. | Wait for Retry-After, then retry. |
500 | Something failed on our side. | Retry with backoff. Contact support if it continues. |
502 | A service we depend on failed. | Retry with backoff. |
503 | We're temporarily unavailable. | Wait for Retry-After if it's present, then retry. |
504 | An operation timed out. | Retry with backoff. |
A 404 covers another account's resources too, so you can't tell whether an ID exists elsewhere. A path ID that isn't a valid UUID returns 400.
Error codes
Authentication and access
| Code | Status | Meaning |
|---|---|---|
UNAUTHORIZED | 401 | The key is missing, malformed, wrong, revoked or expired. The response doesn't say which. |
FORBIDDEN | 403 | The key lacks the required permission, or its owner's role or account doesn't allow this. |
AUTH_ERROR | 500 | Authentication couldn't be completed. Retry. |
Requests and resources
| Code | Status | Meaning |
|---|---|---|
VALIDATION_ERROR | 400 | A parameter or body failed a check. details.issues lists the fields when the check is on a field. |
NOT_FOUND | 404 | The resource or route wasn't found. |
CONFLICT | 409 | A duplicate, such as a contact's phone number or an audience's name, or settings that changed since you read them. |
PAYLOAD_TOO_LARGE | 413 | The body is over the limit. |
UNSUPPORTED_MEDIA_TYPE | 415 | The Content-Type isn't supported. |
RECORDING_NOT_READY | 409 | The recording has no audio to download yet. |
BUSINESS_RULE_ERROR | 422 | The request can't be completed in the resource's current state. |
RATE_LIMIT_EXCEEDED | 429 | The key used its allowance, or this IP address sent too many bad keys. See Rate limits. |
EXECUTION_CAPACITY_EXCEEDED | 429 | Too many of your calls are already queued or in progress. Comes with Retry-After: 60. |
Idempotency
These apply to POST /v1/calls. See Idempotency.
| Code | Status | Meaning |
|---|---|---|
MISSING_IDEMPOTENCY_KEY | 400 | The Idempotency-Key header is missing. |
INVALID_IDEMPOTENCY_KEY | 400 | The key isn't 1 to 255 letters, numbers, dashes or underscores. |
IDEMPOTENCY_KEY_REUSED | 409 | This key was already used with a different body. Nothing was placed. |
REQUEST_IN_PROGRESS | 409 | The first request with this key is still running. Wait, then retry with the same key. |
DUPLICATE_REQUEST | 409 | This key was used before and its stored response isn't available. |
IDEMPOTENCY_UNAVAILABLE | 503 | Requests can't be deduplicated right now, so nothing was placed. Retry with the same key shortly. |
Placing calls
POST /v1/calls returns these when it refuses a call before queueing it.
| Code | Status | Meaning |
|---|---|---|
ACCOUNT_LOCKED | 403 | The account is locked and can't place calls. |
AGENT_INACTIVE | 422 | The agent isn't active. |
AGENT_NOT_READY | 422 | The agent isn't set up to place calls. |
PHONE_NUMBER_AGENT_MISMATCH | 422 | The phone number is assigned to a different agent. |
PHONE_NUMBER_UNASSIGNED | 422 | The phone number isn't assigned to an agent. |
PHONE_NUMBER_INACTIVE | 422 | The phone number isn't active. |
PHONE_NUMBER_OUTBOUND_UNSUPPORTED | 422 | The phone number can't place outbound calls. |
PHONE_NUMBER_COMPLIANCE_PENDING | 422 | The phone number is awaiting regulatory approval. |
PHONE_NUMBER_COMPLIANCE_BLOCKED | 422 | The phone number failed regulatory approval. |
PHONE_NUMBER_NOT_SYNCED | 422 | The phone number is still being set up, or needs setting up again. |
PHONE_NUMBER_NOT_READY | 422 | The phone number's setup isn't finished. |
DESTINATION_NOT_ALLOWED | 422 | This phone number isn't allowed to call that destination. |
DESTINATION_NOT_READY | 422 | The number being called is one of your own, and it isn't ready to receive a call. |
CALL_NOT_READY | 422 | The call can't be placed right now, for another reason. |
Every 422 in this table except AGENT_INACTIVE includes details.action.
Payments
| Code | Status | Meaning |
|---|---|---|
INSUFFICIENT_BALANCE | 402 | The wallet is too low for this request. details has balance, requiredBalance and shortfall. |
PAYMENT_DECLINED | 402 | The payment was declined. |
POST /v1/calls checks your balance after it accepts a call, so a low balance there appears as a failure_code. See Failure codes.
Server failures
| Code | Status | Meaning |
|---|---|---|
INTERNAL_ERROR | 500 | An unexpected failure. |
UPSTREAM_UNAVAILABLE | 502 | A service this request depends on is unavailable. |
SERVICE_UNAVAILABLE | 503 | The service is temporarily unavailable. |
TIMEOUT | 504 | The request timed out. |
When we can't check your key, you get 503 SERVICE_UNAVAILABLE with Retry-After: 5. Nothing is wrong with the key, so retry it and don't replace it. Other 503 responses may not have the header, so use your own backoff for those.
Body limits
A request body can be up to 256 KB. PUT /v1/agents/:id/custom-tools allows 2 MB. A larger body returns 413 PAYLOAD_TOO_LARGE before the key is checked.
Bodies must be JSON with Content-Type: application/json.
| Problem | Result |
|---|---|
The Content-Type header is missing on a request with a body | 400 VALIDATION_ERROR |
The Content-Type isn't a supported type | 415 UNSUPPORTED_MEDIA_TYPE |
| The JSON is malformed | 400 VALIDATION_ERROR |
| The JSON is nested more than 64 levels deep | 400 VALIDATION_ERROR |
| A value or query parameter has a NUL character or invalid Unicode | 400 VALIDATION_ERROR |
Some fields have tighter limits of their own, listed on each resource's page.
Handle errors in code
curl -s -w "\nstatus: %{http_code}\n" https://backend.synthesys.app/api/v1/agents \
-H "SYNTH_API_KEY: YOUR_API_KEY"For common failures and fixes, see Troubleshooting.