API reference

Errors & rate limits

Conventional HTTP status codes, a stable machine-readable error code on every failure, and a per-key rate limit with standard headers.

Error format

Failures return a JSON body with an error.code you can branch on and a human-readable message.

json
{
  "error": {
    "code": "window_closed",
    "message": "The 24h window is closed — send an approved template instead"
  }
}

Status codes

StatusCodeMeaning
400validation_errorThe request body or query is wrong — the message says which field.
401unauthenticated / invalid_api_keyMissing, malformed, or revoked key.
403missing_scopeThe key lacks the scope this endpoint requires.
404template_not_found / not_foundUnknown template, message, contact, automation, run, or private link.
409window_closedFree-form text sent outside the 24h window — send a template.
409opted_outThe contact has opted out of messages.
422invalid_phoneThe phone number is not valid E.164.
402 / 403subscription_inactive / quota_exceededPlan issue — check billing.
429rate_limitedToo many requests — slow down.
500internal_errorAn unexpected server error. Safe to retry with backoff.
5xx—WhatsApp/Meta upstream error. Safe to retry with backoff.

Automation errors

Starting an automation — with POST /automations/:id/enroll or a private link — returns 409 when the automation or your workspace’s guardrails stop it. The code says which one, and whether a retry can ever succeed.

CodeMeaningRetry?
not_publishedThe automation isn’t live.After you publish it
no_triggerThe automation has no active trigger. For a private link: its trigger is no longer “From another app”.After you fix the trigger
concurrentThe contact is already part-way through this automation.After their current run ends
reentryThe contact has already been through it.Never
opted_outThe contact unsubscribed.Never
laneAnother automation in the same group is already running for them.Later
quotaYour plan’s automation run limit is used up.After an upgrade or the next billing period
contact_loopThis contact was started too many times recently.Later, not straight away

Retry guidance

  • Retry 429 after the Retry-After seconds, and 5xx with exponential backoff.
  • Don’t blindly retry 4xx — fix the request instead.
  • For automation 409s, follow the Retry? column — reentry and opted_out will never succeed.

Sends are not idempotent

A retry after a timeout may double-send. When in doubt, check GET /messages/:id (or the inbox) before resending.

Rate limiting

Each key is limited to 120 requests per minute (fixed window). Every response includes:

http
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 22        # seconds until the window resets

On 429, honor Retry-After. For large syncs, batch and spread requests out — contact upserts are cheap but still count against the limit.

MywaHey 👋 got a question? Say hi, a real human replies here.now