Errors & limits

How the WhatsApp API reports failures — the response envelope, the actionable send-error codes, the 429 rate-limit shape — and every hard limit that governs media, templates, broadcasts and the 24-hour window. Read this alongside Webhooks & events, where the same failures resurface asynchronously in whatsapp.message.status.


The response envelope

Every WhatsApp endpoint returns the standard Telenow envelope. Success carries data (a few acknowledgements — mark-read, pause/cancel on the app key, template delete — return just success); failure carries a human-readable error string and an HTTP status code.

{ "success": true,  "data": { "wamid": "wamid.HBgL…" } }
{ "success": false, "error": "This contact hasn't messaged you in 24 hours — send an approved template to reopen the conversation." }
StatusWhenWhat to do
400Malformed body, wrong/multiple content fields, or an actionable send error (a plain-English sentence — below)Fix the request as the message says
401Missing or invalid key: Invalid or revoked API key, API key creator is no longer active, or Access token is required (no key sent, or an org key on a login-only route)Check Authorization: Bearer vai_app_… or X-API-Key: vai_live_… — see Authentication
403App key lacks the required scope; or, with an org API key: another org's id in the path (This API key is scoped to a different organization.), a viewer key on a write (This API key's role ('viewer') is read-only on this endpoint.), or the key's creator lacks the roleGrant the scope (Scopes), or use a key with the right org and role
404Unknown channel, template, campaign, or media idCheck the id belongs to your org
404Native WhatsApp is not enabled (app key), or WhatsApp Hub is not enabled / WhatsApp is not enabled (org key), on every route of that surfaceWhatsApp isn't enabled on this Telenow deployment — a platform-wide flag, not a bad id. Contact your administrator
409A campaign isn't in a state that allows the action, or a request with the same Idempotency-Key is still runningRe-read the campaign; or wait and retry (Retry-After: 5)
413Upload exceeds the media cap for its type, or a body over 5 MB sent with Idempotency-KeyShrink or re-encode — media caps
415 / 422A body that isn't JSON or has the wrong types — e.g. a mediaId that isn't a UUID. These come back as plain text, not the JSON envelopeFix the body's shape
429Rate limit exceededBack off using retryAfter — rate limits
502WhatsApp or the number's provider refused the request, or Telenow refused a send it knows can't work (a caption on audio, media on a non-Cloud number, a STOP'd number). Body: { "success": false, "error": "<the provider's message>", "code": "provider_error" }Read error — it's the provider's own explanation

The send endpoint validates the body strictly: POST /send needs exactly one content field (message — body on the org-key route — or mediaId, location, reaction, contacts, interactive). Zero or two of them is a 400 before the message ever reaches WhatsApp.


Send error codes

When the failure matches one of the known Meta codes in the table below, Telenow surfaces it as an actionable 400 on the send call — you don't have to wait for a webhook to know it failed. The 400's error is a plain-English sentence explaining the fix; it doesn't include the code number. Any other failure returns 502 with code: "provider_error" and the provider's own message in error (see the status table). The same codes also appear in the errors array of a whatsapp.message.status event when a message fails after the API already returned a wamid (async delivery failures).

CodeMeaningFix
131042Payment issue — the account can't be billed for this messageAdd or fix a payment method in Meta Business Manager
13104724-hour customer service window has closedSend an approved template instead of free-form text
131026Message undeliverable — recipient isn't on WhatsApp or is restrictedVerify the number is a real WhatsApp user; drop it from retries
131056Sending too fast to this recipient (pair rate limit)Back off and retry after a short delay
368 / 131031Number restricted for a policy violationThe number/account is limited by Meta — resolve the policy issue in Business Manager
132000, 132001, 132005, 132007, 132012, 132015, …Template problems — each gets its own message: the wrong number of values, no template by that name and language, a value too long, a policy breach, a value in the wrong format, a template paused for qualityCheck the template is APPROVED and your variables/components match its placeholders

On the variables path of a template send, a template problem comes back as a 502 whose error starts whatsapp template send failed (400 Bad Request): followed by the same explanation.

Example — sending free-form text after the window closed:

curl -X POST https://api.telenow.ai/api/app-whatsapp/send \
  -H "Authorization: Bearer vai_app_…" \
  -H "Content-Type: application/json" \
  -d '{ "channelId": "9f2c7b1e-…", "to": "+919876543210",
        "message": "Just following up!" }'
{ "success": false, "error": "This contact hasn't messaged you in 24 hours — send an approved template to reopen the conversation." }

The fix is a template send, which is allowed outside the window:

curl -X POST https://api.telenow.ai/api/app-whatsapp/send-template \
  -H "Authorization: Bearer vai_app_…" \
  -H "Content-Type: application/json" \
  -d '{ "channelId": "9f2c7b1e-…", "to": "+919876543210",
        "template": "order_update", "language": "en_US", "variables": ["Asha", "#1234"] }'
{ "success": true, "data": { "wamid": "wamid.HBgLMTIxNTU1NTk4NzYVAgERGBI0Rjc5…" } }

Rate limits

Two limits apply, and a request has to fit both:

  • Per IP address — every key: 2,000 requests per 15 minutes for any request that isn't signed in to the dashboard, app keys and org API keys alike. Over it you get { "success": false, "error": "Too many requests, please try again later", "retryAfter": 412 } — retryAfter is the seconds until the 15-minute window resets, so it can be minutes.
  • Per app — app keys only: roughly 20 requests/second with a burst of 60 for each installed app: { "success": false, "error": "Data API rate limit reached — slow down", "retryAfter": 1 }.

Both 429s also send a Retry-After header. Respect it before retrying, and serialize sends per recipient to avoid the per-pair 131056. Request bodies are capped at 2 MB on JSON routes; the media upload route allows up to 101 MB and the template-media route 17 MB (the file itself is still bound by the per-type media caps below). The org-key limits are listed in full under Authentication & channels.

Safe retries with Idempotency-Key

Send an Idempotency-Key header (any unique string, such as a UUID) on a POST, and a retry with the same key and body within 24 hours returns the original successful response — marked Idempotent-Replay: true — instead of sending twice. The same key with a different body returns 422; the same key while the first request is still running returns 409 with Retry-After: 5. With the header, the request body is limited to 5 MB (413 body too large), so leave it off large media uploads. Without the header, dedupe on the returned wamid and only retry when you did not receive one.


Media caps

Uploads to POST /api/app-whatsapp/channels/{id}/media (or the org-key …/whatsapp-hub/channels/{channelId}/media) are capped per media type (taken from the upload's MIME type; image/webp counts as a sticker). Over the cap returns 413.

TypeMax size
Image5 MB
Audio16 MB
Video16 MB
Sticker500 KB
Document100 MB

Template header media (POST /channels/{id}/templates/media) is separately capped at 16 MB and limited to image/jpeg, image/png, video/mp4, application/pdf. See Media and Templates.


Template limits

Enforced at template creation and edit time (POST / PATCH template endpoints, scope whatsapp:templates). A violation returns 400 before the template is submitted to Meta.

RuleLimit
Body text≤ 1024 characters
Footer text≤ 60 characters
Header text≤ 60 characters
Button text≤ 25 characters
Buttons per template≤ 10
Body countExactly one non-empty BODY (marketing/utility)
PlaceholdersContiguous {{1}}…{{N}} in the body text — no gaps (Meta also expects an example for each; Telenow doesn't check those)
nameLowercase letters, digits and _, ≤ 512 chars
categoryMARKETING · UTILITY · AUTHENTICATION

Authentication templates follow their own shape rules instead of the length limits. Editing a template resets it to PENDING re-approval (a template that's still PENDING can't be edited); name and language are immutable. Full component shapes are in Templates.

Not yet supported: carousel and limited-time-offer templates, and one-tap OTP buttons (authentication templates support COPY_CODE only).


Broadcast limits

Broadcasts are bounded differently on each surface:

RuleApp key (POST /campaigns, scope whatsapp:campaign)Org API key (/whatsapp-campaigns)
Recipients≤ 5,000 per campaign≤ 20,000 per upload (uploads can repeat); ≤ 5,000 per saved-audience call
Active campaigns50 per appNo cap
Throttle60 messages/minute by default (throttle_per_min, 1–6,000); at most ~400/minute actually sentSame

Beyond these, your real ceiling is Meta's messaging tier for the number (e.g. TIER_1K, TIER_10K) — watch whatsapp.account.health for tier changes.


Which numbers support what

A WhatsApp number in Telenow is either a Meta Cloud number (native — "set up for you" — or connected through your own Meta app), a partner number (GupShup, Plivo, AiSensy), or a WhatsApp Web number (linked by QR).

App keyOrg API key
TextMeta Cloud; WhatsApp Web with the whatsapp:web scopeEvery number
Media, interactive, location, contacts, reactionsMeta CloudMeta Cloud
TemplatesMeta CloudMeta Cloud and partner numbers
BroadcastsMeta CloudMeta Cloud and partner numbers
Read conversationsMeta CloudEvery number
Template management, account health, business profileMeta CloudMeta Cloud
Webhook events (received, status)Meta Cloud numbers only, whichever key you use

Meta also limits how often you can change a number's display name — check Meta Business Manager before renaming.


The 24-hour window

WhatsApp only allows free-form messages (text, media, interactive, location, reaction, contacts) within 24 hours of the customer's last inbound message. Outside that window you must send a pre-approved template — free-form sends fail with Meta code 131047, either right away (Telenow's 400) or as a failed status event.

  • Every inbound whatsapp.message.received resets the 24-hour clock.
  • Templates are always allowed, in or out of the window — that's how you re-open a conversation.
  • Reading history via threads shows lastInboundAt (last_inbound_at on the org-key route); use it to decide whether the window is open before sending free-form.
customer replies ──► 24h window OPEN ──► free-form text OK ──┐
                                                             │  24h elapse, no reply
                                                             ▼
                                          window CLOSED ──► free-form → 131047
                                                             │
                                                             └─► send a template to re-open

Reading failure detail from webhooks

A send can succeed synchronously (you got a wamid) yet fail later at Meta — for example, the number turned out to be undeliverable. That surfaces only as a whatsapp.message.status event with status: "failed" and the same numeric codes in its errors array:

{
  "event": "whatsapp.message.status",
  "wamid": "wamid.HBgLMTIxNTU1NTk4NzYVAgARGBI3Q0Ez…",
  "status": "failed",
  "recipient": "+919876543210",
  "errors": [ {
    "code": 131026,
    "title": "Message undeliverable.",
    "message": "Message undeliverable.",
    "error_data": { "details": "Message failed to send because the recipient's phone number is not a WhatsApp phone number." },
    "href": "https://developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes/"
  } ]
}

Match the wamid to your original send, look the code up in the table above, and take the same corrective action you would for a synchronous 400. Read error_data.details for the actionable specifics — it's more precise than the generic title. See Webhooks & events for wiring, signatures and dedupe.


Account health

Many send failures are downstream of your account's state — an unverified business, a low quality rating, or no payment method. Check a Meta Cloud number's live health with your org API key (any role, including viewer) or a dashboard login:

curl "https://api.telenow.ai/api/orgs/{orgId}/whatsapp-cloud/channels/{channelId}/health" \
  -H "X-API-Key: vai_live_…"
{ "success": true, "data": {
  "phone": { "quality_rating": "UNKNOWN", "whatsapp_business_manager_messaging_limit": "TIER_1K", "name_status": "PENDING_REVIEW" },
  "waba":  { "account_review_status": "APPROVED", "business_verification_status": "verified",
             "health_status": { "can_send_message": "BLOCKED", "entities": [ … ] } },
  "tokenStatus": "active",
  "warnings": [
    { "code": "wa_payment_method", "level": "error",
      "title": "There's a problem with your WhatsApp payment method",
      "detail": "There is an error with the payment method. … Please add a new payment method to the account. (Meta error 141006, on WhatsApp Business Account.)",
      "action": "Open Meta Business Manager" }
  ],
  "callingReadiness": { "canEnable": false, "blockers": [ … ], "advisories": [ … ],
                        "callingOn": null, "mediaPlane": true }
} }
  • phone / waba — the raw Meta health fields (quality rating GREEN/YELLOW/RED/UNKNOWN, messaging limit, review and business-verification status). Meta deprecated messaging_limit_tier in favour of whatsapp_business_manager_messaging_limit, which reports your business portfolio's limit; both are read, newest first. An absent limit means Meta hasn't rated the account — not that it passes.
  • wabaId / metaBusinessId — the WhatsApp Business Account and Meta business behind the number. The response can also carry notes.
  • tokenStatus — active, or revoked if Meta pulled access (you'll need to reconnect the number).
  • warnings — a ready-made, human-readable list; each has a code, a level (info / warn / error), a title, a detail, and an optional action. The dashboard renders these as the account-health banner on the channel. Codes include wa_payment_method, wa_token_revoked, wa_quality_rating, wa_business_unverified, wa_account_review, wa_account_blocked, wa_account_notice, wa_name_pending_review, wa_messages_webhook_missing and wa_template_webhook_missing. On native numbers, anything Meta reports inside health_status.entities[] — both errors[] and the advisory additional_info[] — is flattened into this list, including notices on an entity that otherwise reads AVAILABLE.
  • callingReadiness — native numbers only; see Calling errors.

The same signals also arrive asynchronously as whatsapp.account.health webhook events — a nudge to re-read this endpoint. A payment method is the most common blocker: without one, sends fail with 131042 — add it in Meta Business Manager (Meta bills you directly).


Calling errors

Enabling WhatsApp calling has prerequisites that Meta only reveals by refusing. The toggle checks them before asking Meta, so you get the reason rather than a bare rejection — callingReadiness.blockers on the health endpoint is the same list.

Meta codeOur codeWhat it meansWho fixes it
138015wa_calling_messaging_limitYour business portfolio's messaging limit is under 2,000/24 h. It rises with business verification and sending history — not by adding numbersyou
138018wa_calling_webhook_missingNo app subscribed to your WABA subscribes to the calls webhook field, or the account isn't subscribed to us at allus — contact support
141006wa_payment_methodNo valid payment method on the WABA. Blocks business-initiated conversations; shown as a warning, and it does not by itself stop the toggleyou
131044—The same payment problem, arriving as an error webhook during a live call rather than as a refusal to enableyou
—wa_calling_media_offThe server can't carry call audio yet, so calls are declined immediately rather than answered. A warning, not a refusalus
—wa_token_revokedMeta pulled access — reconnect WhatsAppyou

blockers stop the toggle; advisories are real and shown but never fail a save Meta would have accepted. A blocker is only ever something Meta itself documents as rejecting the request, so the pre-flight can't forbid something Meta would allow.