Response shape
{ "message_uuid": "...", "wamid": "..." }
{
"code": "WINDOW_CLOSED",
"message": "The 24-hour messaging window is closed. Send an approved template to reopen it."
}
{ code, message } — both fields are guaranteed.
Switch on
code: it is stable and part of the contract. The message is
helpful human text and may be refined between API versions without notice.
Messages are delivered in English (industry standard, same pattern as
Stripe, GitHub or Linear). If you need to display them translated to the
end user, map them in your client by code.Status families
| HTTP | Meaning | Retry? |
|---|---|---|
2xx | Success — 200 read, 201 created, 202 accepted (async), 207 multi-status. | — |
4xx | Request error — auth, validation, or missing resource. | No, fix it first. |
429 | Rate limit exceeded. | Yes, after the Retry-After. |
5xx | Server error. | Yes, with backoff. |
Code catalog
The HTTP status of each code is typical: a specific endpoint may return a
different status if context warrants it. The authoritative per-endpoint status
lives in the API Reference. The table here is a general reference, not a
binding per-endpoint contract.The conversation-resolution ambiguity codes (
PHONE_NUMBER_AMBIGUOUS,
USERNAME_AMBIGUOUS) can surface on any endpoint that accepts a
conversation reference by phone/username, even if its API Reference lists
other 409 codes.Auth and tenant
| Code | HTTP | Description |
|---|---|---|
INVALID_API_TOKEN | 401 | The token does not exist or has been revoked. Generate a new one in Settings → API Token. |
TOKEN_BUSINESS_MISMATCH | 403 | The token does not belong to the business slug in the URL. Verify they refer to the same business. |
Rate limit
| Code | HTTP | Description |
|---|---|---|
RATE_LIMIT_EXCEEDED | 429 | You exceeded the 60 req/min bucket. Wait the seconds from the Retry-After header. See Rate limits. |
Validation
| Code | HTTP | Description |
|---|---|---|
INVALID_REQUEST | 400 | The body failed schema validation (missing field, wrong type, value out of range). |
INVALID_CURSOR | 400 | The pagination cursor is invalid or expired. Restart the listing without a cursor. |
BATCH_LIMIT_EXCEEDED | 400 | The action group exceeds the maximum allowed in a single request. The message carries the actual cap. |
Conversation, contact, and channel
| Code | HTTP | Description |
|---|---|---|
CONVERSATION_NOT_FOUND | 404 | The conversation identifier (uuid, whatsapp_user_id, username, or phone) does not resolve to any conversation in the business. |
CONTACT_NOT_FOUND | 404 | The contact identifier does not resolve to any contact in the business. |
PHONE_NUMBER_AMBIGUOUS | 409 | Several contacts share that phone number (recycled numbers / separate identities). Identify the conversation by whatsapp_user_id or uuid. |
USERNAME_AMBIGUOUS | 409 | Several contacts share that username. Identify the conversation by whatsapp_user_id or uuid. |
CHANNEL_NOT_CONFIGURED | 422 | The conversation has no WhatsApp channel configured. Connect one in the dashboard before sending. |
TESTER_CONVERSATION_NOT_WRITABLE | 422 | Tester conversations do not accept real WhatsApp sends. Use a real one, or restrict the action to tags/notes/mailbox. |
TRANSCRIPTION_REQUIRED_TO_RESOLVE | 422 | POST /conversation/resolve: the conversation has inbound media awaiting transcription. Complete the manual transcription from the dashboard and retry. |
Tags
| Code | HTTP | Description |
|---|---|---|
TAG_NOT_FOUND | 404 | The tag does not exist for this business. |
TAG_ALREADY_ASSIGNED | 409 | The tag is already assigned to this conversation. |
TAG_NOT_ASSIGNED | 404 | The tag is not assigned, so it cannot be removed. |
TAG_AMBIGUOUS | 409 | Multiple tags match that name. Use tag_uuid to disambiguate. |
TAG_BUSINESS_MISMATCH | 400 | The tag belongs to a different business. |
CANNOT_ASSIGN_DEFAULT_TAG | 422 | The tag is system-managed (handled by the backend, e.g. the pending-conversion tag) and cannot be assigned via the API. |
CANNOT_REMOVE_DEFAULT_TAG | 422 | The tag is system-managed (handled by the backend, e.g. the pending-conversion tag) and cannot be removed via the API. |
Mailboxes and categories
| Code | HTTP | Description |
|---|---|---|
CATEGORY_NOT_FOUND | 404 | The mailbox category does not exist for this business. |
CATEGORY_BUSINESS_MISMATCH | 403 | The category belongs to a different business. |
AMBIGUOUS_CATEGORY | 409 | Multiple categories match that name. Use category_uuid to disambiguate. |
MAILBOX_NOT_FOUND | 404 | The mailbox does not exist for this business. |
MAILBOX_ALREADY_ASSIGNED | 409 | The conversation is already in that mailbox. |
AMBIGUOUS_MAILBOX | 409 | Multiple mailboxes match that name. Use mailbox_uuid to disambiguate. |
NO_MAILBOXES_IN_CATEGORY | 422 | The category has no mailboxes configured. Add at least one in the dashboard. |
Precondition guards
| Code | HTTP | Description |
|---|---|---|
WINDOW_CLOSED | 422 | The 24h window is closed. Send an approved template to reopen it. |
WALLET_BLOCKED | 402 | The token wallet for the required AI model is empty or blocked. Top up to continue. |
VISION_WALLET_BLOCKED | 402 | The vision token wallet is empty. Vision processing requires top-up. |
VISION_BLOCKED | 402 | The thread’s vision pipeline is blocked. Resolve the pending media in the dashboard before retrying. |
NO_AI_EMPLOYEE_ASSIGNED | 422 | No AI employee is assigned to this conversation. Assign one before running the action. |
Owner ↔ executor coherence (schedules)
| Code | HTTP | Description |
|---|---|---|
OWNER_MISMATCH | 422 | The scheduled message’s executor does not match the conversation’s current owner. Reassign or cancel the schedule first. |
AI employees and execution
| Code | HTTP | Description |
|---|---|---|
AI_EMPLOYEE_NOT_FOUND | 404 | The AI employee does not exist for this business. |
AMBIGUOUS_AI_EMPLOYEE | 409 | Multiple employees match that name. Use ai_employee_uuid to disambiguate. |
AI_EMPLOYEE_ALREADY_ASSIGNED | 409 | That AI employee is already assigned to the conversation. |
INVALID_ASSIGNEE_FOR_RUN_AI | 409 | The conversation is human-owned and no ai_employee was specified. Pass one or reassign an AI first. |
AI_EXECUTION_FAILED | 500 | The AI execution failed unexpectedly. Retry; if it persists, check the employee’s dashboard. |
ACTIONS_DEPTH_EXCEEDED | 422 | Chained AI actions exceed the maximum depth allowed in a single request. |
Scheduled message action
| Code | HTTP | Description |
|---|---|---|
SCHEDULED_MESSAGE_ACTION_REQUIRED | 422 | There is an active scheduled message on the conversation. Specify scheduled_message_action (reassign or cancel). |
SCHEDULED_MESSAGE_ACTION_INVALID | 400 | The scheduled_message_action value is invalid. Must be reassign or cancel. |
Uploads
| Code | HTTP | Description |
|---|---|---|
INVALID_FILE_TYPE | 400 | The file type is not allowed. See the documentation for the list of accepted MIME types. |
FILE_TOO_LARGE | 400 | The file exceeds the maximum size for its category. |
STORAGE_QUOTA_EXCEEDED | 413 | The business has reached its storage quota. Free up space or upgrade the plan. |
STORAGE_QUOTA_CLEANUP_INSUFFICIENT | 507 | Storage is full and automatic cleanup could not free enough space for this upload. Free up space or upgrade the plan. |
FILE_NOT_FOUND | 404 | No file matches that file_uuid for this business. |
FILE_NOT_UPLOADED | 422 | The file was registered but the upload was never confirmed. Re-upload before referencing it. |
SIZE_MISMATCH | 422 | The actual size of the uploaded file does not match the size declared in request-upload. |
MIME_MISMATCH | 422 | The actual MIME type does not match the declared one. Re-upload with the correct Content-Type. |
UPLOAD_FAILED | 500 | The upload could not be completed due to a storage error. Retry. |
Messages
| Code | HTTP | Description |
|---|---|---|
PREPARE_FAILED | 500 | The message could not be prepared (DB or upstream error). Retry. |
SEND_FAILED | 502 | Sending to WhatsApp failed. Check the channel’s health and retry. |
BSUID_SEND_DISABLED | 409 | The contact has no phone number, and sending via its BSUID (business-scoped user ID) is disabled for this business. Use a contact with a phone number or enable BSUID sending. |
Quick replies and templates
| Code | HTTP | Description |
|---|---|---|
QUICK_REPLY_NOT_FOUND | 404 | The quick reply does not exist for this business. |
QUICK_REPLY_AMBIGUOUS | 409 | Multiple quick replies match that name. Use quick_reply_uuid to disambiguate. |
QUICK_REPLY_EMPTY | 422 | The quick reply has no actions configured. Add at least one in the dashboard. |
TEMPLATE_NOT_FOUND | 404 | The template does not exist for this business. |
TEMPLATE_NOT_APPROVED | 422 | The template is not approved by Meta. Approval is required before sending. |
AUTH_TEMPLATE_REQUIRES_PHONE | 409 | An authentication template cannot be sent to a contact without a phone number: Meta requires it. Send to a contact with a phone number. |
TEMPLATE_AMBIGUOUS | 409 | Multiple templates match that name. Specify language to disambiguate. |
TEMPLATE_VARIABLES_INVALID | 422 | The body variables do not match the template (wrong count, missing index, or empty value). |
TEMPLATE_HEADER_MISMATCH | 400 | The header type does not match what the template declares (e.g. template expects an image but you sent text). |
TEMPLATE_FAKE_REQUIRES_TESTER_CONVERSATION | 422 | This template is a fake (test-only) template and can only be sent to tester conversations. |
Generic operations
| Code | HTTP | Description |
|---|---|---|
FETCH_FAILED | 500 | Could not load the data required to complete this request. Retry. |
UPDATE_FAILED | 500 | The change could not be applied (DB error). Retry. |
UNKNOWN_ERROR | 500 | Unexpected error. Contact support if it persists. |