Skip to main content

Troubleshooting: WhatsApp Flows API — 502 META_API_ERROR

The Flows list (GET /api/v1/whatsapp/flows) and Flows create endpoints proxy Meta’s Graph API. When Meta’s side of the call fails, Orbit returns a deliberately mapped error rather than passing Meta’s raw body through — so a 502 on these endpoints is not a raw upstream crash, it is a classified envelope you can decode. This page covers that envelope. If the problem is a Flow’s runtime (screen render, encrypted exchange) work Flow execution failures instead; if the WABA itself is disconnected, work Troubleshoot a WhatsApp connection.
The reconnect, publish-state, and token-recovery levers this page points at are all tenant-owned — Orbit’s role is to classify the upstream response so you know which lever to pull.

Symptom map

Work the table top-down — it covers the failure modes that account for nearly every 502 on the Flows endpoints.

Decode the META_API_ERROR variants

A 502 with code: "META_API_ERROR" bundles different upstream failures behind one code. The Messenger channel page uses the same pattern for its MESSAGE_SEND_FAILED 502 — read details.provider_message first, then pick the fix. Here the discriminator is details.isRateLimited: The dashboard Flows tab already stops auto-retrying when details.isRateLimited: true — if you built your own poller against the list endpoint, apply the same gate or you will burn the WABA’s call budget.

Test whether the failure is retryable

Run this decision in order — the first branch that matches is the answer:
  1. details.isRateLimited: true. Do not retry in a loop. Wait for Meta’s call-budget window to reset (a few minutes), then send one retry. Every extra request while throttled extends the window.
  2. Code META_API_ERROR, no rate-limit tag. Retryable. Send the retry with an Idempotency-Key header so a create call cannot double-create a Flow, and add jitter (a random 0–30 seconds on top of your backoff) so a fleet of pollers does not retry in lockstep.
  3. Code WHATSAPP_CONNECTION_INVALID or WHATSAPP_TOKEN_EXPIRED. Not retryable — the connection or token is deterministic-broken, so a retry returns the same thing. Reconnect from Settings → Channels → WhatsApp and retry only after the reconnect reports Connected.
  4. A 4xx body-validation error on the create endpoint with none of the codes above. Not retryable until the request body is fixed — re-check the Flow JSON field-by-field before resubmitting.

Rate-batching guidance

The throttled-WABA variant is nearly always self-inflicted — the Flows list endpoint is cheap to call in a loop, and Meta counts that loop against the WABA’s call budget. Keep the budget intact:
  • Poll the list endpoint on a schedule (minutes, not seconds) from server-side jobs, never from a re-render loop in a client.
  • Page with offset instead of re-requesting the whole list — each page call also counts against the budget.
  • When you are creating Flows in bulk, serialize the creates with a small delay rather than firing them concurrently.
  • If a campaign the Flows tab drives needs frequent refresh, throttle the refresh on details.isRateLimited rather than on a fixed timer.

Escalation payload

If you have worked the page and the same 502 persists across a WABA that is not throttled, email whatsapp-support@devotel.io with these three items so we can trace the upstream call without a back-and-forth:
  1. The request id from the response headers of a failing call.
  2. The variant branch — isRateLimited true vs absent, and the exact code and message you received.
  3. Whether the same request has succeeded before — a Flows list that never succeeds is a different trace than one that broke recently.

What not to do

  • Do not retry on a throttled WABA. details.isRateLimited: true is the signal to stop; every further request extends the window.
  • Do not reconnect on a 502 META_API_ERROR. Only WHATSAPP_CONNECTION_INVALID or WHATSAPP_TOKEN_EXPIRED resolves with a reconnect; reconnecting on a plain transient blip changes nothing.
  • Do not reuse an Idempotency-Key with a different body. Key reuse with a different payload is rejected; regenerate the key when the payload changes.

Platform-side 5xx: FLOWS_ERROR

Every decoder above starts from a response Meta actually returned. A FLOWS_ERROR means the exchange did not get that far: when Orbit cannot complete its own side of a Flows call (the proxy hop to Meta, or the read that assembles the answer), the Flows endpoints return a 500 with an opaque envelope:
The message names the operation that failed: list, create, get, update (screen upload), publish, delete, or the funnel and submissions reads the flow detail page performs. In the dashboard, the WhatsApp Flows pages under Messages → WhatsApp → Flows surface the same body as an error toast.

FLOWS_ERROR vs the Meta-side codes above

If details is present you are not looking at this envelope, whatever the status code. FLOWS_ERROR is by construction the opaque case: no error_subcode, no validation_errors, no rate-limit tag.

Responding to a FLOWS_ERROR

  • Reads (list, get, funnel, submissions): retry directly. They are idempotent and change nothing on Meta.
  • Create, update, publish, delete: back off, resend the same payload, then verify. Confirm against the flows list (or the flow detail page) that the operation landed before resending again. A FLOWS_ERROR means the operation did not complete, so the same payload is safe to resend; the verify step is what guarantees a retry never double-applies a create or a publish.
  • Nothing is billed on a FLOWS_ERROR. The failure precedes any billable send.
  • Do not reconnect the channel. As with the 502 above, the token and connection are not what failed, so reconnecting changes nothing.

When to escalate

A FLOWS_ERROR that clears on the next retry needs no action. If the same operation keeps returning FLOWS_ERROR across several minutes, email whatsapp-support@devotel.io with the request_id and timestamp from the error’s meta block, the operation that failed, and whether it succeeds a moment later — that combination traces the proxy hop without a back-and-forth.

See also