Skip to content

Errors

Every error uses the same envelope: status is "error", message is readable text, and error_code is a stable value to branch on.

{
  "status": "error",
  "message": "to_number is not valid E.164",
  "error_code": "invalid_phone_number",
  "data": { "field": "to_number" }
}

Codes

CodeHTTPMeaning
invalid_api_key401The x-api-key header is missing or the key does not exist.
key_revoked401The key was revoked in the portal.
key_expired401The key passed its expiry date.
invalid_token401The portal session expired — sign in again.
account_suspended403The workspace is suspended. Contact support.
invalid_phone_number422A number is not E.164 (+ country code, digits only). data.field says which.
missing_required_field422A required field is missing or empty. data.field says which.
invalid_request400The body is not valid JSON, or a field has the wrong type.
content_too_long422The text is longer than 10 SMS segments.
premium_rate_blocked403Premium-rate destinations are not allowed.
duplicate_request409This request_id was already used; data has the original message id.
no_device_available503No online phone is paired with from_number. Open the app on the phone and check it has internet.
new_account_limit429New workspaces can send 50 live messages in their first 24 hours.
rate_limit_exceeded429Too many requests — wait for the Retry-After seconds.
not_found404The id does not exist in your workspace.
invalid_url422Webhook URL must be public HTTPS.
invalid_events422Unknown webhook event name.
no_recipients422A campaign has no one left to send to (all opted out or blocked).
too_many_recipients422A campaign is limited to 10,000 recipients.
internal_error500Unexpected server error. Retry once; if it persists, contact support.

Retrying

  • 429 and 503 are safe to retry after a pause.
  • 4xx validation errors will fail the same way until the request is fixed.
  • Pass a request_id when sending so a retry after a timeout can never send twice.