Reference
Errors
HTTP status codes, the response envelopes, and every error message and code the gateway returns, grouped by where it comes from.
HTTP status codes
| Status | Meaning |
|---|---|
| 200 | The request was understood. Read success (or status on the balance endpoints): a business refusal such as an unknown sender ID, an empty bundle or a closed WhatsApp session window also comes back as 200. |
| 400 | A required field is missing or has the wrong type. Nothing was sent. The WhatsApp endpoints name the field in a sentence (recipients must be a list); the others use the validator's Missing required keys text. |
| 401 | The API key is missing, unknown, disabled, expired or used from a disallowed IP. |
| 406 | XML endpoint only: the Accept header does not allow an XML response. |
| 429 | The key has exceeded its requests-per-minute limit. |
| 5xx | The gateway failed. Retry with the same user_reference_id so a repeat cannot double-send. |
200
- Meaning
- The request was understood. Read success (or status on the balance endpoints): a business refusal such as an unknown sender ID, an empty bundle or a closed WhatsApp session window also comes back as 200.
400
- Meaning
- A required field is missing or has the wrong type. Nothing was sent. The WhatsApp endpoints name the field in a sentence (recipients must be a list); the others use the validator's Missing required keys text.
401
- Meaning
- The API key is missing, unknown, disabled, expired or used from a disallowed IP.
406
- Meaning
- XML endpoint only: the Accept header does not allow an XML response.
429
- Meaning
- The key has exceeded its requests-per-minute limit.
5xx
- Meaning
- The gateway failed. Retry with the same user_reference_id so a repeat cannot double-send.
The error envelope
On the send endpoints an error is the normal envelope with success: false and the reason in message; data is absent, except on the WhatsApp endpoints, where a refusal can still carry data (each recipient's reason, or the original message_id behind a duplicate). The balance endpoints use status instead of success. The legacy XML endpoint answers in XML with status-code -6. Each group below says which shape applies.
{
"message": "Invalid From Sender ID",
"success": false,
"api_reference": "1764184470578998353"
}Match on the message text exactly as listed; the gateway does not return separate numeric codes. Quote api_reference when you contact support about a specific call. The validator's messages (Missing required keys, Expected STRING) and the WhatsApp endpoints' 400 sentences name the field, so they are safe to log but not to parse.
Authentication
HTTP 401 (429 for the rate limit) with {"message": "<code>", "success": false, "api_reference": "..."}. JSON on every endpoint, including the XML one.
| message | Meaning |
|---|---|
| Missing API key. Provide X-API-Key header or Authorization: Bearer <api_key> | Neither header was present. |
| Invalid authorization format. Use 'Bearer <api_key>' or 'Basic <base64>' | The Authorization header used another scheme. |
| Invalid Basic auth format. Use api_key as username | Basic credentials decoded, but neither side of the colon held a key. |
| Invalid Base64 encoding in Basic auth | The Basic credentials were not valid base64. |
| AUTHENTICATION_FAILED | The key does not match any credential. |
| CREDENTIAL_DISABLED | The key exists but was switched off. |
| CLIENT_NOT_FOUND | The key is not attached to an account. Contact support. |
| CLIENT_DISABLED | Your account is suspended. Contact support. |
| CREDENTIAL_EXPIRED | The key's expiry date has passed. |
| IP_NOT_ALLOWED | The request came from an address outside the key's allow-list. |
| RATE_LIMIT_EXCEEDED | More requests this minute than the key allows (HTTP 429). Wait for the next minute. |
Missing API key. Provide X-API-Key header or Authorization: Bearer <api_key>
- Meaning
- Neither header was present.
Invalid authorization format. Use 'Bearer <api_key>' or 'Basic <base64>'
- Meaning
- The Authorization header used another scheme.
Invalid Basic auth format. Use api_key as username
- Meaning
- Basic credentials decoded, but neither side of the colon held a key.
Invalid Base64 encoding in Basic auth
- Meaning
- The Basic credentials were not valid base64.
AUTHENTICATION_FAILED
- Meaning
- The key does not match any credential.
CREDENTIAL_DISABLED
- Meaning
- The key exists but was switched off.
CLIENT_NOT_FOUND
- Meaning
- The key is not attached to an account. Contact support.
CLIENT_DISABLED
- Meaning
- Your account is suspended. Contact support.
CREDENTIAL_EXPIRED
- Meaning
- The key's expiry date has passed.
IP_NOT_ALLOWED
- Meaning
- The request came from an address outside the key's allow-list.
RATE_LIMIT_EXCEEDED
- Meaning
- More requests this minute than the key allows (HTTP 429). Wait for the next minute.
SMS (JSON)
HTTP 200 with {"message": "<code>", "success": false, "api_reference": "..."}. Validation failures use the same body with HTTP 400.
| message | Meaning |
|---|---|
| Invalid From Sender ID | from is not a sender ID on your account. |
| From Sender ID not allowed | The sender ID exists but is not active. |
| Recipient list size is invalid | to is empty or has more than 2,000 entries. |
| INSUFFICIENT_BUNDLE | Not enough SMS units for this send. |
| INSUFFICIENT_BUNDLE: no international SMS bundle | A recipient outside Zambia needs an international SMS bundle you do not have. |
| duplicate user reference id | user_reference_id was already used with this recipient and sender ID. |
| Invalid parameters provided | A recipient row failed validation: message too long, non-GSM characters, or similar. |
| Missing required keys: ["message"] | HTTP 400. A required field is absent; the list names them. |
| Expected STRING, got INTEGER 260971234567, at to -> index 0 | HTTP 400. A field has the wrong type; numbers must be strings. |
Invalid From Sender ID
- Meaning
- from is not a sender ID on your account.
From Sender ID not allowed
- Meaning
- The sender ID exists but is not active.
Recipient list size is invalid
- Meaning
- to is empty or has more than 2,000 entries.
INSUFFICIENT_BUNDLE
- Meaning
- Not enough SMS units for this send.
INSUFFICIENT_BUNDLE: no international SMS bundle
- Meaning
- A recipient outside Zambia needs an international SMS bundle you do not have.
duplicate user reference id
- Meaning
- user_reference_id was already used with this recipient and sender ID.
Invalid parameters provided
- Meaning
- A recipient row failed validation: message too long, non-GSM characters, or similar.
Missing required keys: ["message"]
- Meaning
- HTTP 400. A required field is absent; the list names them.
Expected STRING, got INTEGER 260971234567, at to -> index 0
- Meaning
- HTTP 400. A field has the wrong type; numbers must be strings.
SMS (XML, legacy)
HTTP 200, text/xml, <response status-code="-6"><message>CODE</message></response>.
| message | Meaning |
|---|---|
| INVALID_SENDER_ID | from is not a sender ID on your account. |
| INVALID_RECIPIENT_LIMIT | to is empty or has more than 2,000 numbers. |
| INSUFFICIENT_BUNDLE_LIMIT | Not enough SMS units for this send. |
| DUPLICATE_MSG_REF | msg_ref was already used for this recipient. |
| INVALID_REQUEST | A recipient row failed validation (message length, characters, or a reused msg_ref). |
| Missing required keys: ["source"] | A required field is absent. Same status-code -6. |
INVALID_SENDER_ID
- Meaning
- from is not a sender ID on your account.
INVALID_RECIPIENT_LIMIT
- Meaning
- to is empty or has more than 2,000 numbers.
INSUFFICIENT_BUNDLE_LIMIT
- Meaning
- Not enough SMS units for this send.
DUPLICATE_MSG_REF
- Meaning
- msg_ref was already used for this recipient.
INVALID_REQUEST
- Meaning
- A recipient row failed validation (message length, characters, or a reused msg_ref).
Missing required keys: ["source"]
- Meaning
- A required field is absent. Same status-code -6.
HTTP 200 with {"message": "<code>", "success": false, "api_reference": "..."}. Validation failures use the same body with HTTP 400.
| message | Meaning |
|---|---|
| Invalid Sender | from is not an email sender on your account. |
| Sender not allowed | The sender exists but is not active. |
| Invalid Recipient Limit | to is empty or has more than 2,000 entries. |
| Insufficient bundle | Not enough email units for this send. |
| duplicate user reference id | user_reference_id was already used with this address and sender. |
| Invalid parameters provided | An address is not valid, the subject is over 200 characters, or the body is empty or over 100,000 characters. |
Invalid Sender
- Meaning
- from is not an email sender on your account.
Sender not allowed
- Meaning
- The sender exists but is not active.
Invalid Recipient Limit
- Meaning
- to is empty or has more than 2,000 entries.
Insufficient bundle
- Meaning
- Not enough email units for this send.
duplicate user reference id
- Meaning
- user_reference_id was already used with this address and sender.
Invalid parameters provided
- Meaning
- An address is not valid, the subject is over 200 characters, or the body is empty or over 100,000 characters.
HTTP 200 with {"success": false, "message": "<code>", "api_reference": "..."}, plus "data" when there is something to report: each recipient's reason after NO_VALID_RECIPIENTS, or the original message_id after DUPLICATE_MSG_REF. Nothing was queued.
| message | Meaning |
|---|---|
| INVALID_SENDER_ID | from is not a WhatsApp sender on your account. |
| SENDER_ID_NOT_ALLOWED | The sender exists but is not approved. |
| SENDER_REQUIRED | from was omitted and your account has more than one approved WhatsApp sender. |
| TEMPLATE_NOT_FOUND | No template with that name (and language) on your account. |
| TEMPLATE_NOT_APPROVED | The template is not APPROVED by Meta, or has been deactivated. |
| LANGUAGE_REQUIRED | language was omitted and the template is approved in more than one language. |
| TEMPLATE_SENDER_MISMATCH | The template belongs to a different WhatsApp Business Account than the sender. |
| INSUFFICIENT_BUNDLE_LIMIT | Your WhatsApp balance does not cover the accepted recipients at the template's category. |
| NO_VALID_RECIPIENTS | Every recipient was rejected; data.results says why for each. |
| SESSION_WINDOW_CLOSED | Session endpoint: no open 24-hour window with that number on the sender. Send a template instead. |
| REPLY_TO_NOT_FOUND | Session endpoint: reply_to is neither a sent message_id of yours nor a WhatsApp message id (wamid.). |
| REACTION_TARGET_NOT_FOUND | Session endpoint: reaction.message_id is neither a sent message_id of yours nor a WhatsApp message id (wamid.). |
| DUPLICATE_MSG_REF | Session endpoint: user_reference_id was already used for that number from that sender; data.message_id is the original. |
| INVALID_NUMBER | Session endpoint: to does not parse as a phone number. |
| INVALID_USER_REFERENCE_ID | Session endpoint: user_reference_id is not 1 to 100 characters. |
| MEDIA_URL_INVALID | Session endpoint: media.url is not an https URL. |
| SEND_FAILED | The gateway could not write the messages. Retry with the same user_reference_id values. |
INVALID_SENDER_ID
- Meaning
- from is not a WhatsApp sender on your account.
SENDER_ID_NOT_ALLOWED
- Meaning
- The sender exists but is not approved.
SENDER_REQUIRED
- Meaning
- from was omitted and your account has more than one approved WhatsApp sender.
TEMPLATE_NOT_FOUND
- Meaning
- No template with that name (and language) on your account.
TEMPLATE_NOT_APPROVED
- Meaning
- The template is not APPROVED by Meta, or has been deactivated.
LANGUAGE_REQUIRED
- Meaning
- language was omitted and the template is approved in more than one language.
TEMPLATE_SENDER_MISMATCH
- Meaning
- The template belongs to a different WhatsApp Business Account than the sender.
INSUFFICIENT_BUNDLE_LIMIT
- Meaning
- Your WhatsApp balance does not cover the accepted recipients at the template's category.
NO_VALID_RECIPIENTS
- Meaning
- Every recipient was rejected; data.results says why for each.
SESSION_WINDOW_CLOSED
- Meaning
- Session endpoint: no open 24-hour window with that number on the sender. Send a template instead.
REPLY_TO_NOT_FOUND
- Meaning
- Session endpoint: reply_to is neither a sent message_id of yours nor a WhatsApp message id (wamid.).
REACTION_TARGET_NOT_FOUND
- Meaning
- Session endpoint: reaction.message_id is neither a sent message_id of yours nor a WhatsApp message id (wamid.).
DUPLICATE_MSG_REF
- Meaning
- Session endpoint: user_reference_id was already used for that number from that sender; data.message_id is the original.
INVALID_NUMBER
- Meaning
- Session endpoint: to does not parse as a phone number.
INVALID_USER_REFERENCE_ID
- Meaning
- Session endpoint: user_reference_id is not 1 to 100 characters.
MEDIA_URL_INVALID
- Meaning
- Session endpoint: media.url is not an https URL.
SEND_FAILED
- Meaning
- The gateway could not write the messages. Retry with the same user_reference_id values.
WhatsApp per-recipient reasons
Template endpoint only, in data.results[].reason next to status REJECTED. The other recipients were still sent: the HTTP status is 200 and success is true unless every recipient was rejected.
| reason | Meaning |
|---|---|
| INVALID_NUMBER | to is missing or does not parse as a phone number. |
| INVALID_USER_REFERENCE_ID | user_reference_id is not a string or number of 1 to 100 characters. |
| DUPLICATE_MSG_REF | user_reference_id was already used for this number from this sender, in this request or an earlier one. message_id is the message it went out as. |
| BODY_PARAMETERS_INVALID | body does not give exactly the template's variables, or a value is blank or over 1,024 characters. |
| HEADER_PARAMETER_INVALID | The header's variable or media is missing, of the wrong type, or given for a template without a header slot. |
| MEDIA_URL_INVALID | The header media url is not an https URL. |
| BUTTON_PARAMETERS_INVALID | A dynamic url button is missing its value, or a button names an index or type the template does not have. |
| OTP_REQUIRED | An AUTHENTICATION template was sent without otp. |
| OTP_INVALID | otp is not 1 to 15 letters and digits, or was given for a template of another category. |
INVALID_NUMBER
- Meaning
- to is missing or does not parse as a phone number.
INVALID_USER_REFERENCE_ID
- Meaning
- user_reference_id is not a string or number of 1 to 100 characters.
DUPLICATE_MSG_REF
- Meaning
- user_reference_id was already used for this number from this sender, in this request or an earlier one. message_id is the message it went out as.
BODY_PARAMETERS_INVALID
- Meaning
- body does not give exactly the template's variables, or a value is blank or over 1,024 characters.
HEADER_PARAMETER_INVALID
- Meaning
- The header's variable or media is missing, of the wrong type, or given for a template without a header slot.
MEDIA_URL_INVALID
- Meaning
- The header media url is not an https URL.
BUTTON_PARAMETERS_INVALID
- Meaning
- A dynamic url button is missing its value, or a button names an index or type the template does not have.
OTP_REQUIRED
- Meaning
- An AUTHENTICATION template was sent without otp.
OTP_INVALID
- Meaning
- otp is not 1 to 15 letters and digits, or was given for a template of another category.
WhatsApp request shape
HTTP 400 with {"success": false, "message": "<sentence>", "api_reference": "..."}. The message names the field. Nothing was queued.
| message | Meaning |
|---|---|
| recipients must be a list | Template endpoint: recipients is missing or not an array. |
| recipients must hold between 1 and 1000 entries | Template endpoint: the array is empty or too long. |
| template is required | Template endpoint: template is missing or empty. |
| from must be a string | from was given as something other than a string. |
| language must be a string | Template endpoint: language was given as something other than a string. |
| callback_url must be an https URL of at most 300 characters | callback_url is not https, has no host, or is too long. |
| to is required | Session endpoint: to is missing or empty. |
| type must be one of text, image, document, audio, video, sticker, interactive, location, contacts, reaction | Session endpoint: type is missing or something else. |
| text is required for a text message | Session endpoint: type is text and text is missing. |
| text must hold 1 to 4096 characters | Session endpoint: text is blank or too long. |
| media.url is required when type is <type> | Session endpoint: a media type without media.url; the message names the type sent. |
| media.caption is not allowed on <type> | Session endpoint: a caption on an audio or sticker message; the message names the type. |
| media.caption must be a string | Session endpoint: the caption has another type. |
| media.caption must hold at most 1024 characters | Session endpoint: the caption is too long. |
| media.filename must be a string | Session endpoint: filename has another type. |
| reply_to must be a string | Session endpoint: reply_to has another type. |
| reply_to is not allowed on a reaction | Session endpoint: a reaction already points at its message; it cannot quote one too. |
| interactive is required when type is interactive | Session endpoint: type is interactive and the interactive object is missing. |
| interactive.type must be button or list | Session endpoint: interactive.type is missing or something else. |
| interactive.body must hold 1 to 1024 characters | Session endpoint: the interactive body is missing, blank or too long. |
| interactive.buttons must hold between 1 and 3 entries | Session endpoint: a button message without 1 to 3 buttons. |
| interactive.buttons ids must be unique | Session endpoint: two buttons share an id. interactive.buttons titles must be unique and interactive.sections[].rows ids must be unique are the same check on titles and list rows. |
| interactive.sections must hold between 1 and 10 entries | Session endpoint: a list message without 1 to 10 sections. |
| interactive.sections must hold at most 10 rows in total | Session endpoint: the list's sections hold more than 10 rows together. |
| location is required when type is location | Session endpoint: type is location and the location object is missing. |
| location.latitude must be a number between -90 and 90 | Session endpoint: the latitude is missing, not a number or out of range. location.longitude must be a number between -180 and 180 is its twin. |
| contacts must be a list | Session endpoint: contacts is not an array. |
| contacts must hold between 1 and 10 entries | Session endpoint: the array is empty or too long. |
| contacts[].name.formatted_name is required | Session endpoint: a contact card without a name object or a formatted name. |
| contacts[].phones must hold at least one phone | Session endpoint: a contact card with no phones entry that has a phone. |
| reaction is required when type is reaction | Session endpoint: type is reaction and the reaction object is missing. |
| reaction.message_id must hold 1 to 1000 characters | Session endpoint: the reaction names no message. |
| reaction.emoji must be a single emoji, or empty to remove a reaction | Session endpoint: emoji is missing, or longer than one character. |
| <field> must hold <min> to <max> characters | Session endpoint: a required text in a structured object is missing, blank or outside its limits; the message names the field and the limits (interactive.buttons[].title must hold 1 to 20 characters, interactive.sections[].rows[].title must hold 1 to 24 characters, contacts[].name.formatted_name must hold 1 to 1000 characters). |
| <field> must hold at most <max> characters | Session endpoint: an optional text in a structured object is too long (interactive.header, interactive.footer, interactive.sections[].rows[].description, location.name, location.address). |
| <field> must be a string | Session endpoint: an optional text in a structured object has another type. |
| <list> entries must be objects | Session endpoint: an entry of interactive.buttons, interactive.sections, interactive.sections[].rows or contacts is not an object. |
| message_ids must be a list | Status endpoint: message_ids is missing or not an array. |
| message_ids must hold between 1 and 100 entries | Status endpoint: the array is empty or too long. |
| message_ids must be strings | Status endpoint: an entry is not a non-empty string. |
recipients must be a list
- Meaning
- Template endpoint: recipients is missing or not an array.
recipients must hold between 1 and 1000 entries
- Meaning
- Template endpoint: the array is empty or too long.
template is required
- Meaning
- Template endpoint: template is missing or empty.
from must be a string
- Meaning
- from was given as something other than a string.
language must be a string
- Meaning
- Template endpoint: language was given as something other than a string.
callback_url must be an https URL of at most 300 characters
- Meaning
- callback_url is not https, has no host, or is too long.
to is required
- Meaning
- Session endpoint: to is missing or empty.
type must be one of text, image, document, audio, video, sticker, interactive, location, contacts, reaction
- Meaning
- Session endpoint: type is missing or something else.
text is required for a text message
- Meaning
- Session endpoint: type is text and text is missing.
text must hold 1 to 4096 characters
- Meaning
- Session endpoint: text is blank or too long.
media.url is required when type is <type>
- Meaning
- Session endpoint: a media type without media.url; the message names the type sent.
media.caption is not allowed on <type>
- Meaning
- Session endpoint: a caption on an audio or sticker message; the message names the type.
media.caption must be a string
- Meaning
- Session endpoint: the caption has another type.
media.caption must hold at most 1024 characters
- Meaning
- Session endpoint: the caption is too long.
media.filename must be a string
- Meaning
- Session endpoint: filename has another type.
reply_to must be a string
- Meaning
- Session endpoint: reply_to has another type.
reply_to is not allowed on a reaction
- Meaning
- Session endpoint: a reaction already points at its message; it cannot quote one too.
interactive is required when type is interactive
- Meaning
- Session endpoint: type is interactive and the interactive object is missing.
interactive.type must be button or list
- Meaning
- Session endpoint: interactive.type is missing or something else.
interactive.body must hold 1 to 1024 characters
- Meaning
- Session endpoint: the interactive body is missing, blank or too long.
interactive.buttons must hold between 1 and 3 entries
- Meaning
- Session endpoint: a button message without 1 to 3 buttons.
interactive.buttons ids must be unique
- Meaning
- Session endpoint: two buttons share an id. interactive.buttons titles must be unique and interactive.sections[].rows ids must be unique are the same check on titles and list rows.
interactive.sections must hold between 1 and 10 entries
- Meaning
- Session endpoint: a list message without 1 to 10 sections.
interactive.sections must hold at most 10 rows in total
- Meaning
- Session endpoint: the list's sections hold more than 10 rows together.
location is required when type is location
- Meaning
- Session endpoint: type is location and the location object is missing.
location.latitude must be a number between -90 and 90
- Meaning
- Session endpoint: the latitude is missing, not a number or out of range. location.longitude must be a number between -180 and 180 is its twin.
contacts must be a list
- Meaning
- Session endpoint: contacts is not an array.
contacts must hold between 1 and 10 entries
- Meaning
- Session endpoint: the array is empty or too long.
contacts[].name.formatted_name is required
- Meaning
- Session endpoint: a contact card without a name object or a formatted name.
contacts[].phones must hold at least one phone
- Meaning
- Session endpoint: a contact card with no phones entry that has a phone.
reaction is required when type is reaction
- Meaning
- Session endpoint: type is reaction and the reaction object is missing.
reaction.message_id must hold 1 to 1000 characters
- Meaning
- Session endpoint: the reaction names no message.
reaction.emoji must be a single emoji, or empty to remove a reaction
- Meaning
- Session endpoint: emoji is missing, or longer than one character.
<field> must hold <min> to <max> characters
- Meaning
- Session endpoint: a required text in a structured object is missing, blank or outside its limits; the message names the field and the limits (interactive.buttons[].title must hold 1 to 20 characters, interactive.sections[].rows[].title must hold 1 to 24 characters, contacts[].name.formatted_name must hold 1 to 1000 characters).
<field> must hold at most <max> characters
- Meaning
- Session endpoint: an optional text in a structured object is too long (interactive.header, interactive.footer, interactive.sections[].rows[].description, location.name, location.address).
<field> must be a string
- Meaning
- Session endpoint: an optional text in a structured object has another type.
<list> entries must be objects
- Meaning
- Session endpoint: an entry of interactive.buttons, interactive.sections, interactive.sections[].rows or contacts is not an object.
message_ids must be a list
- Meaning
- Status endpoint: message_ids is missing or not an array.
message_ids must hold between 1 and 100 entries
- Meaning
- Status endpoint: the array is empty or too long.
message_ids must be strings
- Meaning
- Status endpoint: an entry is not a non-empty string.
Retrying
400and401: fix the request or the key; a retry with the same input gets the same answer.429: wait for the next minute. The limit is per credential and per calendar minute; see Rate limits.200withsuccess: false: the message names what to change (top up, register the sender ID, shorten the text). Retrying unchanged does not help, except thatduplicate user reference id(orDUPLICATE_MSG_REFon WhatsApp, which also names the originalmessage_id) means an earlier attempt already went through. A WhatsAppSESSION_WINDOW_CLOSEDis answered by sending a template instead.5xxor a timeout: retry with the sameuser_reference_id. If the first attempt did get through, the retry is refused as a duplicate rather than sent twice.