REST API v1 · Reference
Errors
What each status means, and the codes in the error body. Every endpoint answers this way.
API reference · Errors
HTTP statuses
| Status | Meaning | When you see it |
|---|---|---|
| 400 | Bad Request | Missing or invalid parameters — e.g. invalid_template_parameters names the offending variable |
| 401 | Unauthorized | No credentials, or api_key_invalid / api_key_revoked / api_key_expired for a rejected key |
| 403 | Forbidden | insufficient_scope — the key lacks the scope this endpoint needs (`required` names it); ip_not_allowed — the key is limited to other addresses; forbidden — the role is too low for this request (sending needs agent, admin-only writes need admin); or instagram_disabled — Instagram isn’t available for your workspace |
| 404 | Not Found | The requested message, template or webhook endpoint does not exist in your workspace |
| 402 | Plan Limit | A plan limit is reached: this month’s outgoing-message allowance is used up (quota_exceeded), or the contact limit is full, so a NEW lead cannot be added (recapturing an existing lead still works); or instagram_addon_required — the workspace hasn’t added the Instagram add-on (`offer` says what the owner can buy: add_addon or upgrade_plan) |
| 409 | Conflict | contact_opted_out, a duplicate webhook URL (the body names the existing endpoint’s id), the 25-endpoint cap, Instagram not connected (not_connected, reconnect_required), WhatsApp Pay not set up, number_banned / sending_blocked (Meta is refusing messages from the number, so nothing was queued), or a broadcast to people who have never messaged you that needs confirmNewRecipients or has to wait (new_recipients_unconfirmed, new_recipient_cool_off, follow_up_too_soon) |
| 413 | Payload Too Large | A request body above its limit: 64 KB for ordinary writes, 1 MB for bulk writes, and a media upload above 16 MB (8 MB for an Instagram photo) |
| 422 | Unprocessable | outside_24h_window — a free-form send (WhatsApp or Instagram) needs an open customer service window; on WhatsApp use a template. needs_phone_number — a verification-code template to a customer WhatsApp shares no number for. Also invalid_post / invalid_schedule for an Instagram post, and notice_to_new_recipients / follow_up_notice_template for a broadcast of a utility or authentication template to people who have never messaged you |
| 429 | Rate Limited | You have exceeded the token bucket for this endpoint — wait the Retry-After seconds, then retry. With several keys in a workspace, each key may use at most half of a bucket, so one runaway integration cannot starve the others |
| 503 | Briefly Unavailable | rate_limiter_unavailable — a broadcast, a template submission, a webhook secret rotation, erasing a contact, a bulk write, deleting a template, disconnecting an integration or sending an invoice is paused while its safety limiter cannot be reached. Retry after Retry-After |
| 500 | Server Error | An internal error occurred — retry with exponential backoff |
Error codes
Every error body has an error field. Branch on it, not on the HTTP status or the message, which is written for people and may change. A validation error can carry an object there instead of a code. These are the codes you will meet most:
| error | Status | What it means |
|---|---|---|
| unauthorized | 401 | No API key was sent. |
| api_key_invalid · api_key_revoked · api_key_expired | 401 | The key was sent and refused. Make a new key on the API keys page. |
| insufficient_scope | 403 | The key lacks the scope this endpoint needs; `required` names it. |
| ip_not_allowed | 403 | The key is limited to other addresses. |
| forbidden | 403 | The key’s role is too low for this request. |
| plan_feature_unavailable | 402 | The plan does not include this; `upgradeUrl` says where to change it. |
| plan_limit_reached · quota_exceeded | 402 | A plan limit is full: contacts, documents, or this month’s messages. |
| invalid | 400 | The body failed validation; `message` and `issues` say which field. |
| invalid_json | 400 | The body is not JSON. |
| invalid_reference | 400 | An id in the body points at nothing in your workspace. |
| invalid_template_parameters | 400 | A template variable is missing or blank; `issues` names it. |
| template_header_media_required | 400 | The template has an image, video or document header and none was sent. |
| cannot_send_to_self | 400 | The recipient is your own business number. |
| not_found | 404 | Not in your workspace (some endpoints spell it "not found"). Another workspace’s id answers the same. |
| not_available | 404 | This feature is not open to your workspace yet. |
| unknown_integration | 404 | No integration by that name; GET /integrations lists them. |
| payload_too_large | 413 | The body is over this endpoint’s limit. |
| conflict | 409 | The write clashes with an existing record. |
| contact_opted_out | 409 | The customer opted out; nothing was sent. |
| number_banned · sending_blocked | 409 | Meta is refusing messages from the number, so nothing was queued. |
| not_connected · reconnect_required | 409 | Instagram is not connected, or needs connecting again. |
| not_a_draft | 409 | Only a draft invoice can be edited or deleted; cancel an issued one. |
| no_payload | 409 | That webhook delivery is older than the payload log, so it cannot be sent again. |
| invalid_idempotency_key | 400 | The Idempotency-Key is empty or over 255 characters. |
| idempotency_key_in_progress | 409 | The first request with this key is still running. Retry shortly. |
| idempotency_key_reused | 422 | This key was used with a different request. |
| outside_24h_window | 422 | A free-form message needs an open 24-hour window; on WhatsApp send a template. |
| meta_error | 400 · 429 | Meta refused the call; `code` and `message` are Meta’s own. 429 when Meta is throttling. |
| send_unconfirmed · reply_unconfirmed | 502 | Instagram did not answer, so the message may have gone. Check before sending again; a retry with the same Idempotency-Key gets this answer, not a second send. |
| rate_limited | 429 | The bucket is empty. Wait Retry-After seconds. |
| uploads_in_progress | 429 | Too many uploads are running at once. Wait Retry-After seconds. |
| rate_limiter_unavailable | 503 | An action that costs money or cannot be undone is paused while its limiter is down. Retry after Retry-After. |
| internal_error | 500 | Something failed on our side. Retry with backoff. |
A 429 is covered on Rate limits, and the idempotency codes under Idempotency.