Skip to content
WhatsMarketingDeveloper Hub

Errors

Errors respond with the appropriate HTTP status code and a JSON body. There are two forms:

Business errors (what you asked for can't be done): they carry a human-readable message and, when your code needs to tell them apart, a stable code.

JSON
{ "statusCode": 409, "code": "CONTACT_PHONE_EXISTS", "message": "Ya existe un contacto con ese teléfono.", "contactId": "0b1c2d3e-…", "fullName": "Laura Pérez" }

Validation errors (the body or the parameters don't meet the contract): statusCode 400 and a list of messages, one per field. An unknown field or parameter is also rejected (property x should not exist): that way a typo doesn't silently pass as "no filter".

JSON
{ "statusCode": 400, "message": ["primaryPhone must be shorter than or equal to 32 characters"], "error": "Bad Request" }

HTTP status codes

Code When
400 Validation, or an action that doesn't apply in the current state (campaign already sent, no recipients, insufficient balance, date in the past)
401 Invalid, revoked or expired key
403 Missing scope, or route not open to keys
404 The resource doesn't exist or doesn't belong to your organization
409 Conflict: phone already used by another contact, channel identity already linked, contact with history
429 Rate limit
5xx Our error: retry with exponential backoff and, if it persists, contact us

Stable error codes

code HTTP Meaning
API_KEY_INVALID 401 The key doesn't exist, is mistyped or was revoked
API_KEY_EXPIRED 401 The key has expired: create another one
API_KEY_SCOPE_MISSING 403 The route requires a scope the key doesn't have; the message says which one
API_KEY_NOT_ALLOWED_HERE 403 The route is not open to API keys
API_KEY_RATE_LIMITED 429 More than 600 requests in one minute with that key
CONTACT_PHONE_EXISTS 409 A contact with that phone already exists; the response includes contactId and fullName
CONTACT_HAS_HISTORY 409 A contact with conversations, campaigns or deals can't be deleted
CAMPAIGN_VARIABLES_REQUIRED 400 The template has variables and the campaign doesn't say where they come from
PHONE_NUMBER_NOT_OWNED 409 The sender number doesn't belong to your organization

The remaining business errors come with message only; the most frequent ones are listed in the reference for each route. Treat code values as stable: they won't change within the version. message texts may improve over time and are meant to be displayed, not compared.

Insufficient balance

Sending a campaign charges the batch to the wallet before calling Meta. If the balance isn't enough, POST /campaigns/{id}/send responds 400 with "Saldo insuficiente en la wallet." (insufficient wallet balance) and the campaign stays as it was; if the balance runs out in the middle of a staggered send, the campaign is paused with that reason in stopReason and is resumed with POST /campaigns/{id}/resume after topping up. Check GET /campaigns/{id}/preview-cost before sending: wallet.sufficient tells you without attempting the send.