Every error code the API returns, what causes it, and what to do.
An error is JSON with a code you can branch on and an error message for people. Branch on code. The message can change. Treat a code you do not know as a generic failure.
Some codes add fields to the body, shown below. A failure on the connection to WhatsApp can also add reason, one of proxy_unreachable, proxy_auth_failed, proxy_tls_failed, proxy_timeout, proxy_dns_failed or connect_failed.
How to read the status
Status
Meaning
Retry?
400
your request is wrong
no, fix it
401
token problem
no, fix the token
403
the token is valid but may not do this
no
404
the route or object does not exist
no
409
the channel's state does not allow this
after the state changes
429
back-pressure or an allowance
after the wait
500
the request failed; a write may already have applied
idempotent reads: backoff; writes: inspect before retrying; report if it persists
502, 503
the request or response failed; a write may already have applied
idempotent reads: backoff; writes: inspect before retrying
504
outcome of a send unknown
not blindly, see send_unconfirmed
For writes, a 500, 502 or 503 does not prove nothing happened. The edge can reject a response after the operation succeeded. Inspect before retrying: list or read the state you changed, and never re-create a group without reading/listing first. A send may be delivered even when you cannot confirm it; see Recipes. Only retry a write directly when the specific error proves it was refused before being sent. POST messages/read sends receipts and is a write, despite its name.
400 Bad request
Code
Cause
Fix
invalid_body
the body is not valid JSON, is empty, is over 1 MiB, carries a field the route does not take, lists a webhook receiver without a secret, or has malformed or invalid mentions
send a JSON object with only the documented fields
invalid_request
well-formed but cannot be done as sent, for example more than 50 participants, a duplicate participant, a name that is too long, an X-Wagate-Groups-Fresh value other than required, or a mention whose phone identity cannot be resolved
read the error text, fix the value
field_not_allowed
a key this route does not accept, on routes that name it (channel PATCH, history-sync)
remove the key
header_not_allowed
the request carried an account header
send only the bearer token
up_to_range_too_large
a read with up_to would mark too many messages or send receipts to too many senders
mark an older message with up_to first, or read without up_to; nothing was sent
quote_not_found
the quoted message is not in this channel's log
quote a message this channel has
quote_wrong_chat
the quoted message belongs to another chat
quote a message from the same chat
media_too_large
the media is over the limit for its kind
shrink it, or use document; limits are on Media
media_type_unsupported
not a type this kind can carry
convert it, or send as document
media_fetch_failed
the URL did not answer, answered with an error, or is not a public address
check the URL from outside your network
401 Unauthorized
Code
Cause
Fix
unauthorized
the token is missing, too short, unknown, or does not open this channel; or the channel does not exist
check the token and the channel ID
credential_stale
the token stopped applying during the request: replaced, or the channel removed
use the current token
slot_ended
this channel's paid slot has ended
renew it in the console
A 401 carries WWW-Authenticate: Bearer.
403 Forbidden
Code
Cause
Fix
admin_only_field
your webhook list would drop a receiver whose media setting was set in the console
keep that receiver, or remove it in the console
account_suspended
the account that owns this channel is on hold
contact support
number_forbidden
a supplied phone number could not be read as a valid number
check the number format; invalid mentions answer 400 invalid_body or invalid_request
blocked
this channel is blocked
contact support
channel_unentitled
no paid slot, and not your sandbox channel
buy a channel in the console
media_forbidden
the media belongs to another channel, to nobody, does not exist, or was removed by retention. One answer for all four, on purpose
check the media_id came from this channel
not_own_message
you tried to delete a message this channel did not send
only your own messages can be deleted for everyone
lid_lookup_not_granted
LID lookup is not enabled for this channel; only an operator can enable it
do not retry; contact support if you need it
404 Not found
Code
Cause
Fix
not_found
the route does not exist (a malformed media ID counts), or webhooks/test named a URL this channel has not set
check the path and the URL
message_not_found
the message is not in this channel's log
use an ID from a send result or a webhook
no_history_anchor
the channel holds no message in that chat to ask for older history from
wait for a message, or send one
409 Conflict
Code
Cause
Fix
not_linked
the channel has no linked phone
link it in the console
stood_down
WAgate paused this number after WhatsApp pushed back
reconnect it in the console after the restriction ends
verification_failed
the subgroup WAS created, and checking it afterward failed. group_jid names it
a flood guard refused a burst; the body has window and retry_after
Retry-After seconds
gateway_busy
the gateway is working on too many requests; bound says which limit; nothing is queued
Retry-After seconds
sandbox_allowance_exceeded
the sandbox channel used up an allowance; window says which
until resets_at
daily_send_cap_exceeded
this channel exhausted a positive daily ceiling, has a zero stop, or its send counting became unreliable; the body has window, cap, sent, resets_at and retry_after
positive ceiling: until resets_at; zero cap: contact support; inspect counting errors
whatsapp_rate_limited
WhatsApp throttled the number; no Retry-After is sent
back off, retry later
daily_cap_group_add
today's group-add allowance is used (warm-up protection is on); the body has class, cap, sent, tier and resets_at
until resets_at
daily_cap_contact_write
today's contact-save allowance is used (warm-up protection is on); the body has class, cap, sent, tier and resets_at
until resets_at
daily_cap_contact_check
today's number-check allowance is used (warm-up protection is on); the body has class, cap, sent, tier and resets_at
until resets_at
history_request_too_soon
a history request for this channel or chat came too recently
Retry-After seconds (also retry_after in the body)
Details and example bodies: Limits.
500, 502, 503, 504
Code
Status
Cause
What to do
internal_error
500
WAgate could not complete the request
idempotent reads: backoff; writes: inspect before retrying; contact support if it persists
upstream_unreachable
502
the gateway could not reach the service behind it
idempotent reads: backoff; writes: inspect before retrying
upstream_redirect
502
the service behind it answered with a redirect
idempotent reads: backoff; writes: inspect before retrying
upstream_not_json
502
the service answered with something unreadable
idempotent reads: backoff; writes: inspect before retrying
upstream_not_relayable
502
the answer could not be passed back safely
idempotent reads: backoff; writes: inspect before retrying
upstream_too_large
502
the media object is larger than the public API relays
use a smaller object
media_upload_failed
502
the upload to WhatsApp failed and nothing was sent
safe to retry
receipt_partial
502
some read receipts went out before the read failed; receipted says how many
do not resend the same request
upstream_unavailable
503
the token could not be checked right now
retry
wagate_not_configured
503
this deployment has no upstream configured
contact support
operator_unresolved
503
the gateway could not confirm its own account setup for a moment; nothing is wrong with your channel
retry after Retry-After (5 seconds)
whatsapp_disconnected
503
the WhatsApp connection dropped before the request completed; the response cannot tell whether the write was sent
treat any write as possibly applied; list or read the state of what you changed before retrying a write; never re-create (e.g. a group) without reading/listing first; reads can be retried after a short back-off
whatsapp_timeout
503
WhatsApp did not answer an info query in time; the response cannot tell whether the write was sent
treat any write as possibly applied; list or read the state of what you changed before retrying a write; never re-create (e.g. a group) without reading/listing first; reads can be retried after a short back-off
read_unresolvable
503
a read receipt could not find the other side's address
inspect read/receipt state before retrying this write
history_unresolvable
503
a history read was refused rather than answered short, because the other side's second address could not be resolved
retry
history_unavailable
503
synced history is unavailable right now
retry later
audit_unavailable
503
the read could not be recorded first, so it was not served
retry
lane_reclaimed
503
a call reached its deadline before it finished; the body has ambiguous
see below; Retry-After: 1
send_unconfirmed
504
the outcome of a send is UNKNOWN; it may have reached WhatsApp
send_unconfirmed (504): the message may have been delivered. The body has ambiguous: true and, when WAgate could log the attempt, an id. That id names a row in this channel's chat log marked ambiguous: true. It is not a WhatsApp message ID, so no message.status webhook will carry it, and neither the row nor its absence tells you whether the message went out. Do not resend blindly: check with the recipient where you can, and resend only if you accept a possible duplicate. See Recipes.
lane_reclaimed (503): ambiguous is always present. false means the call was refused before it was sent and nothing reached WhatsApp, so a plain retry is safe. true means it was sent and the outcome is unknown: inspect before retrying a write. For a group add, read membership first; if the participant is already present, treat the add as success.
receipt_partial (502): some receipts went out. Do not send the same request again, or those messages are marked twice. Mark only the rest.
Full handling code for all three is in Recipes.
Warming codes
Code
Status
Meaning
warming_peer_refused
403
Warming refused a partner number. Contact support.
warming_cooldown
409
Stopped for safety. restart_after says when warming can be switched on again, or null when an operator clears it.
warming_ack_required
409
The account has not acknowledged the current warming text. Send acknowledge.
warming_restart_ack_required
409
The cooldown is over. Send restart_ack: true to switch warming back on.