Skip to content

Errors

Every API v2 error is {code, message, details} at the root. The codes you will meet, what they mean, and the details keys that tell you what to do next.

View .md

One shape

JSON
{ "code": "invalid_request", "message": "Invalid request body", "details": { "errors": [{ "path": ["to"], "message": "Required" }] } }

There is no {"error": {...}} wrapper anywhere in v2. Branch on code; show message to a person. A message never carries a stack trace or a source path.

Details keys

Keys that mean the same thing on every error:

KeyMeaning
errorsOn every 400 invalid_request for a bad body or query: [{path, message}], with path naming the field (["to"], ["contacts", 2, "recipient"]).
recommendationOne sentence saying what to do next.
dashboardUrlThe dashboard screen that fixes it.
connectOn 409 no_channel: what this project can connect to reach the recipient, each with the exact request, or null when it is connected from the dashboard.
from / addressThe address the error is about, under the name the request used.

Codes

StatusCodeMeaning
400invalid_requestA field is missing or wrong. See details.errors.
400invalid_jsonThe body is not JSON.
400duplicate_identifierAnother contact already holds this identifier on this channel (details.conflicts).
400invalid_bot_tokenTelegram rejected the bot token (details.telegramSaid). Nothing was connected.
401unauthorizedMissing or invalid API key.
402insufficient_balanceThe balance cannot cover it.
402paid_plan_requiredNeeds a paid plan, for example buying a number.
402spend_cap_reachedThe team's monthly spend cap is reached. details carries capCents, spentCents and dashboardUrl.
403kyb_requiredOne-way SMS is closed to this account until its business verification is approved. Not returned while one-way SMS needs no verification, which is the default. details.dashboardUrl is /kyb.
403ten_dlc_requiredSMS to a +1 number needs an approved 10DLC campaign. details.connect lists the registration calls, details.alternatives the channels that need none.
403channel_inactiveThe channel you named is connected but switched off.
403sending_suspendedSending from this account is suspended. Contact support.
403plan_limit_reachedThe plan has no room for another active connection.
404not_foundThe resource, or the address, is not in this project.
405method_not_allowedKnown path, wrong method. details.allowed and the Allow header list the methods.
409no_channelNothing this call could use can reach the recipient. details.connect says what to connect.
409ambiguous_fromTwo channel groups share the address. Pick one of details.candidates.
409conflictA repeated idempotencyKey: nothing was sent again, and the original is in details.message (or details.campaign). Also a template name this project already uses.
409invalid_statusThe campaign is in a state that does not allow this (details.status).
409email_domain_missing, email_domain_not_verified, domain_setup_failedThe email address cannot be connected yet. details.recommendation has the fix.
409disconnect_unavailableThis transport cannot be removed through the API. The message says what to do instead.
409channel_cannot_receiveAn agent cannot answer on one-way SMS.
422sms_compliance_blockedThe content check refused an SMS, or could not run (the message says which). Nothing was sent or charged. details.requiredFixes and details.suggestedCompliantVersion say what to change. See SMS.
429rate_limitedThe per-project request limit. Wait for Retry-After.
429rate_limit_exceededA narrower limit: one SMS per recipient per minute, or too many agent test runs for the project. The message says when to retry.
429daily_limit_exceededA channel's daily sending ceiling (details.limit). Resets at 00:00 UTC.
503verification_unavailableThe DNS check of an email domain could not run. Nothing changed; retry.

Sending limits, plan allowances and reviews are the same as on v1. See Sending limits.

Retrying a 409

Every 409 is a settled answer: retrying the same request cannot change it. It carries the header x-should-retry: false, on v1 and v2 alike, and the SDKs honour it, so they raise the 409 as a ConflictError at once instead of retrying it. A repeated idempotencyKey is the case to handle on purpose: the original is in the error's body, and that is how a retry learns the first attempt landed.

Wrong method

A path under /v2/messages, /v2/conversations, /v2/contacts, /v2/channels, /v2/templates, /v2/campaigns, /v2/agents, /v2/webhook or /v2/email-domains called with a method it does not take answers 405, not 404:

JSON
{ "code": "method_not_allowed", "message": "PUT is not supported here. This path takes GET, POST.", "details": { "allowed": ["GET", "POST"] } }

An unknown path is 404.