Errors, limits & health

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; failure carries a human-readable error string and an HTTP status code.

{ "success": true,  "data": { "wamid": "wamid.HBgL…" } }
{ "success": false, "error": "24-hour customer service window has closed — send an approved template instead" }
StatusWhenWhat to do
400Malformed body, wrong/multiple content fields, or an actionable send error (codes below)Fix the request per the code
401Missing or invalid app keyCheck Authorization: Bearer vai_app_… — see Authentication
403App key lacks the required scopeGrant the scope at install — Scopes
404Unknown channelId, template, campaign, or media idCheck the id belongs to your org
404Body error is Native WhatsApp is not enabled on every WhatsApp endpoint (incl. GET /channels)The native WhatsApp Cloud API isn't enabled on this Telenow deployment — a platform-wide runtime flag, not a bad id. Contact your administrator
413Upload exceeds the media cap for its typeShrink or re-encode — media caps
429Rate limit exceededBack off using retryAfterrate limits
500An upstream/Meta send failure that doesn't match a known code — returns a generic {"success":false,"error":"Something went wrong"} with no Meta detail (the real error is logged server-side only)Recover the specific code from the whatsapp.message.status webhook

The send endpoint validates the body strictly: POST /send needs exactly one content field (message, mediaId, location, reaction, contacts, or 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; any other upstream/Meta failure returns a generic 500 (see the status table) with no Meta detail — recover the code from the whatsapp.message.status webhook. 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
132000132999Template problem — approval, missing/extra parameters, or a paused templateCheck the template is APPROVED and your variables/components match its placeholders

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": "131047: 24-hour window closed — send an approved template" }

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

The Data API allows roughly 20 requests/second with a burst of 60. Over the limit returns 429 with a retryAfter hint in seconds:

{ "success": false, "error": "Data API rate limit reached — slow down", "retryAfter": 1 }

Respect retryAfter before retrying, and serialize sends per recipient to avoid the per-pair 131056. Request bodies are capped at 2 MB on JSON routes; media upload routes allow up to 101 MB (the file itself is still bound by the per-type media caps below).

No idempotency key. Sends aren't deduplicated server-side by a client token. To make retries safe, key your own 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 are capped per media type (inferred from the file's MIME). 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}} — no gaps, each needs an example
nameLowercase letters, digits and _, ≤ 512 chars
categoryMARKETING · UTILITY · AUTHENTICATION

Editing a template resets it to PENDING re-approval; 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 (POST /campaigns, scope whatsapp:campaign) are bounded per campaign and per app:

RuleLimit
Recipients per campaign≤ 5000
Active campaigns per app50
Default throttle60 messages/minute (throttle_per_min)

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.


Display-name & registration limits

  • Display name: you can change your number's WhatsApp display name at most 10 times per 30 days (Meta limit).
  • Native channels only. The programmatic WhatsApp API operates on native Cloud API numbers set up through Telenow. Numbers linked via WhatsApp Web (QR) or wrapped cloud providers don't expose these endpoints.

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 131047.

  • 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; 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 number's live health (dashboard JWT):

curl "https://api.telenow.ai/api/orgs/{orgId}/whatsapp-cloud/channels/{channelId}/health" \
  -H "Authorization: Bearer <jwt>" -H "X-Org-Id: {orgId}"
{ "success": true, "data": {
  "phone": { "quality_rating": "GREEN", "messaging_limit_tier": "TIER_1K", "name_status": "APPROVED" },
  "waba":  { "account_review_status": "APPROVED", "business_verification_status": "verified", "health_status": "AVAILABLE" },
  "tokenStatus": "active",
  "warnings": [
    { "level": "warn", "title": "Complete business verification",
      "detail": "Your business isn't verified, which caps your messaging limits.",
      "action": "Open Meta Business Manager" }
  ]
} }
  • phone / waba — the raw Meta health fields (quality rating GREEN/YELLOW/RED, messaging tier, review and business-verification status).
  • tokenStatusactive, or revoked if Meta pulled access (you'll need to reconnect the number).
  • warnings — a ready-made, human-readable list; each has 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.

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).