Skip to content

Errors

When a request fails, the API returns an HTTP status of 400 or higher and a JSON body with "success": false, a code and a message. See Requests and responses for the full shape.

{
"success": false,
"code": "api_scope_insufficient",
"message": "API key scope 'read' is insufficient for this endpoint (requires 'send')."
}

Use code in your code, not message. Messages can change. Codes do not change within v1. New codes can be added; handle an unknown code by its HTTP status.

This table lists every code the API returns today. Codes are case-sensitive: most are lowercase, and some older codes on the send endpoints are uppercase.

Code HTTP What it means What to do
api_key_missing
All
401 The Authorization header is missing, does not start with Bearer , or has no key. Send the header exactly as shown in Authentication.
api_key_invalid
All
401 The key is not recognised: it does not start with pk_live_, it is wrong, or it was revoked or rotated. Check that you send the full key. If it was revoked or rotated, use the new key.
api_key_ip_blocked
All
403 The key has an IP allowlist, and the request came from an address that is not on it. Call from an allowed server, or create a key with the right allowlist.
api_scope_insufficient
All
403 The key’s scope does not allow this endpoint. The message says which scope is needed. Use a key with the right scope. See Scopes.
private_auth_restricted
All
403 This key is reserved for an internal SanadWA feature. This happens when a key’s name starts with private-auth:. Create a new key whose name does not start with private-auth:.
api_key_lookup_failed
All
500 SanadWA could not check the key because of a problem on our side. Retry after a few seconds.
rate_limited
All
429 Too many requests with this key, or more than 5 campaign creations in a minute. Nothing was processed. Wait for the seconds in Retry-After. See Rate limits.
not_found
Any path
404 The URL is not an endpoint, or the HTTP method is wrong for it. Check the method, the path and the base URL https://api.sanadwa.com/v1.
INVALID_JSON
JSON bodies
400 The body is not valid JSON. Fix the JSON.
PAYLOAD_TOO_LARGE
JSON bodies
413 The body is larger than 100 KB (1 MB for POST /contacts). Send a smaller body.
validation_error
Writes
400 A field or parameter is missing, has the wrong type or is out of range. The message names it. Fix the field. Check Content-Type: application/json.
idempotency_key_too_long
Writes
400 The Idempotency-Key header is longer than 120 characters. Use a shorter key, for example a UUID.
idempotency_in_progress
Writes
409 A request with the same Idempotency-Key is still running. Wait for Retry-After and retry with the same key.
idempotency_conflict
Writes
409 The Idempotency-Key was already used, successfully, for a different request: another body, or on the contacts, tags and campaigns endpoints another method or path. Nothing was done. Use a new key for a new request. See Idempotency.
phone_not_found
Sends, GET /templates, POST /campaigns
404 The phone_number_id is not an active number in your account. Get valid ids from GET /phone-numbers.
account_not_found
Sends
404 The WhatsApp Business Account of this number is no longer linked to your SanadWA account. Reconnect WhatsApp in the console.
PREPAID_CREDIT_CAP_REACHED
Sends
402 Your account does not have enough credits for this message. details has needed, remaining and shortfall. Nothing was sent. Buy credits in the console, then retry.
SUBSCRIPTION_LAPSED
Sends
402 Your subscription ended recently and there are not enough credits. Nothing was sent. Renew the subscription or buy credits, then retry.
PREPAID_CAP_CHECK_FAILED
Sends
402 SanadWA could not check your balance. Nothing was sent. Retry after a few seconds.
send_failed
Sends
Varies WhatsApp refused the message, or SanadWA could not finish the send. See send_failed below. Read message, upstream_code and meta_error.
TEMPLATE_CATEGORY_UNAVAILABLE
Template send
503 SanadWA could not look up the template’s category. Nothing was sent. Retry after a few seconds.
TRACKED_TEMPLATE_CAMPAIGN_ONLY
Template send
400 The template has click-tracked buttons. Such templates can only be sent in campaigns. Use another template, or send it with POST /campaigns.
TRACKED_TEMPLATE_GUARD_UNAVAILABLE
Template send
503 SanadWA could not check the template. Nothing was sent. Retry after a few seconds.
MARKETING_SUPPRESSED
Template send
409 The recipient does not receive marketing messages from your account (for example, they opted out). The template is a marketing template, or SanadWA does not know its category. Do not send marketing to this person. Use a utility template if the message is not marketing.
SUPPRESSION_CHECK_FAILED
Template send
503 SanadWA could not check the recipient’s marketing preference. Nothing was sent. Retry after a few seconds.
invalid_to_phone
Text send
400 to_phone is not a valid phone number. Send the number in E.164 format, for example +12025550123.
customer_service_window_closed
Text send
409 The recipient has not written to this number in the last 24 hours, so free-form text is not allowed. details has last_inbound_at, window_expires_at and template_required: true. Send a template instead.
message_not_found
GET /messages/{id}
404 No sent message with this id in your account, or the id does not start with wamid.. Check the id. Use the message_id from the send answer.
message_lookup_failed
GET /messages/{id}
500 A problem on our side. Retry after a few seconds.
templates_lookup_failed
GET /templates
500 A problem on our side. Retry after a few seconds.
phone_numbers_lookup_failed
GET /phone-numbers
500 A problem on our side. Retry after a few seconds.
balance_lookup_failed
GET /balance
500 The balance could not be read. The API never reports a balance it could not read. Retry after a few seconds.
tag_already_exists
POST /tags
409 A tag with this name already exists (names are compared without regard to case). Use the existing tag.
tag_not_found
Tags, contacts, campaigns
404 A tag id or tag name does not exist in your account. For a name list, details.tags lists the missing names. Create the tag with POST /tags, or check the id or name.
tags_lookup_failed
GET /tags
500 A problem on our side. Retry after a few seconds.
tag_create_failed
POST /tags
500 A problem on our side. The tag may not have been created. Check with GET /tags, then retry.
tag_delete_failed
DELETE /tags/{id}
500 A problem on our side. Retry after a few seconds.
contact_not_found
DELETE /contacts/{id}
404 No contact with this id in your account. Check the id.
contact_opted_out
DELETE /contacts/{id}
409 The contact opted out of marketing messages. Deleting it would erase the opt-out, so it is refused. details.suppression_state gives the kind of opt-out. Keep the contact.
import_file_missing
POST /contacts/import
400 No CSV file in the file field. Send the file as multipart/form-data in the file field.
import_file_too_large
POST /contacts/import
413 The file is larger than 5 MB. Nothing was saved. Split the file.
import_file_not_csv
POST /contacts/import
415 The file is an Excel workbook. Nothing was saved. Save it as CSV (UTF-8) and upload that.
import_csv_invalid
POST /contacts/import
400 The file is not valid CSV. Nothing was saved. Check its quotes and separators.
import_csv_empty
POST /contacts/import
400 The file has no contact rows. Add rows under the header line.
import_too_many_rows
POST /contacts/import
400 More than 5,000 contact rows. Nothing was saved. details.max_rows is the limit. Split the file.
contacts_lookup_failed
GET /contacts
500 A problem on our side. Retry after a few seconds.
contacts_upsert_failed
POST /contacts
500 A problem on our side. Retry. Writing the same contacts again is safe.
contacts_import_failed
POST /contacts/import
500 A problem on our side. Retry. Importing the same file again is safe.
contact_delete_failed
DELETE /contacts/{id}
500 A problem on our side. Retry after a few seconds.
idempotency_key_required
POST /campaigns
400 The Idempotency-Key header is missing. Nothing was created. Send a unique key per campaign. See Campaigns.
template_variables_invalid
POST /campaigns
400 template_variables does not match the template’s body variables. Give one entry for each body variable, in the template’s order.
header_media_required
POST /campaigns
400 The template has an image, video or document header. Send header_media_url.
header_media_too_large
POST /campaigns
400 The file at header_media_url is too large. Use a smaller file.
tracked_destination_required
POST /campaigns
400 The template has a click-tracked button with no saved destination. Send tracked_destination_url.
tracked_destination_invalid
POST /campaigns
400 tracked_destination_url is not an allowed link. Use a full https link.
tracked_destination_not_applicable
POST /campaigns
400 The template has no click-tracked button. Remove tracked_destination_url.
campaign_invalid
POST /campaigns
400 Another campaign check failed. message explains it. Fix the campaign settings.
insufficient_credits
Campaign create and resume
402 Your credits do not cover every recipient (create) or every message still to send (resume). details has needed, remaining and shortfall. Nothing was created or resumed. Buy credits, then retry.
subscription_lapsed
Campaign create and resume
402 Your subscription ended and there are not enough credits. Renew the subscription or buy credits, then retry.
kyc_required
POST /campaigns
403 Your account is not verified yet. Verify your account in the console under Settings → Verification & privacy.
tracked_links_disabled
POST /campaigns
403 The template has click-tracked buttons, and click tracking is not enabled for your account. Choose another template, or contact support.
template_not_found
POST /campaigns
404 No template with this name and language for this number’s WhatsApp Business Account. Check with GET /templates?phone_number_id=.
campaign_not_found
Campaigns
404 No campaign with this id in your account. Use an id from POST /campaigns or GET /campaigns.
template_not_approved
POST /campaigns
409 The template is not approved. details.template_status gives its status. Wait for approval, or choose an approved template.
campaign_not_active
Campaign pause
409 Only an active campaign can be paused. details.status gives its status. Check the status with GET /campaigns/{id}.
campaign_not_paused
Campaign resume
409 Only a paused campaign can be resumed. details.status gives its status. Check the status with GET /campaigns/{id}.
campaign_cannot_resume
Campaign resume
409 The campaign cannot continue. message says why. Check the campaign in the console.
campaign_busy
Campaign pause and resume
409 Another change to this campaign is in progress. Retry after Retry-After seconds.
audience_empty
POST /campaigns
422 No contact has any of the tags. Nothing was created. Add contacts to the tags, or choose other tags.
audience_all_excluded
POST /campaigns
422 Every contact with the tags is excluded (opted out, paused after a delivery problem, or not a valid WhatsApp number). details.contacts_with_tags gives how many have the tags. Choose other tags.
audience_too_large
POST /campaigns
422 The audience is larger than one campaign allows. Split the audience into smaller campaigns.
balance_check_failed
Campaign create and resume
503 Your balance could not be checked. Nothing was created or changed. Retry after Retry-After seconds (with the same Idempotency-Key for a create).
campaign_create_timeout
POST /campaigns
503 Creating the campaign took too long and was stopped. Nothing was created or sent. Retry with the same Idempotency-Key after Retry-After seconds.
campaign_change_timeout
Campaign pause and resume
503 The change took too long and was stopped. Nothing was changed. Retry after Retry-After seconds.
campaigns_lookup_failed
GET /campaigns
500 A problem on our side. Retry after a few seconds.
campaign_lookup_failed
GET /campaigns/{id}
500 A problem on our side. Retry after a few seconds.
campaign_create_failed
POST /campaigns
500 A problem on our side. The campaign may or may not have been created. Retry with the same Idempotency-Key: it returns the campaign if it was created.
campaign_pause_failed
Campaign pause
500 A problem on our side. Check the status with GET /campaigns/{id}, then retry.
campaign_resume_failed
Campaign resume
500 A problem on our side. Check the status with GET /campaigns/{id}, then retry.

Under each code is where it can happen. “All” means every endpoint. “Writes” means every POST and DELETE endpoint. “Sends” means POST /messages/template and POST /messages/text. “JSON bodies” means requests with a JSON body.

POST /contacts and POST /contacts/import handle each contact on its own and answer 200 even when some contacts were not saved. Each contact that was not saved has its own error code:

Code Meaning What to do
validation_error A field of this contact is missing or wrong; message names it. Fix the contact and send it again.
duplicate_phone The same phone number appears earlier in the same request. Send each phone once.
tag_not_found A tag name on this contact is not in your tag list. Create the tag with POST /tags, then send the contact again.
contact_write_failed The contact could not be saved because of a temporary problem on our side. Send it again.

See Import contacts and tag them.

send_failed means the send did not complete. The HTTP status tells you more:

  • A 4xx status (except 408 and 429) means WhatsApp refused the message. Nothing was sent. The HTTP status is the status that WhatsApp (Meta) returned.
  • A 5xx, 408 or 429 status means the result is not known: the message may or may not have been sent. Read When the result of a send is not known before you retry.
  • A 409 status with the message This send is already being processed., This send requires reconciliation before another attempt. or Send request changed. comes from idempotency, not from WhatsApp.

A send_failed body has these extra fields:

Field Meaning
upstream_code The WhatsApp (Meta) error code as a string, or null when the error did not come from WhatsApp.
meta_error Present when WhatsApp returned an error: title, message, code, subcode, type and fbtrace_id.
recovery Only when WhatsApp refused because of a payment problem on your Meta account (upstream_code 131042). It has a link where you can fix the payment method.
{
"success": false,
"code": "send_failed",
"message": "template name (order_update) does not exist in en",
"upstream_code": "132001",
"meta_error": {
"title": "Template name does not exist in the translation",
"message": "template name (order_update) does not exist in en",
"code": 132001,
"subcode": null,
"type": "OAuthException",
"fbtrace_id": "AbCdEfGhIjKlMnOp"
}
}

For the meaning of WhatsApp error codes, see Meta’s WhatsApp Cloud API error codes. When you contact support, include fbtrace_id.