Skip to main content

WhatsApp API migration parity map

If your app calls Meta’s Cloud API directly, this page maps every call you make to its Orbit equivalent. The WABA migration guide moves the account (number, templates, quality rating); this page moves the code — endpoint by endpoint, payload shape by payload shape. Most teams replace fewer than a dozen call sites. Find yours in the table, port the shape, and the rest of this page covers the webhook and media quirks that differ more than they look.

Migration at a glance

Authentication

One key, no token rotation job, no app-review scope check before each release. Revoke keys from Settings → API Keys the moment a laptop or CI box is decommissioned — you stop editing server env for a new Meta token.

Send messages

Meta nests messaging_product, recipient_type, and the type block under the send URL; Orbit flattens the shape and puts the type up top.

Text message

Two deliberate lambda-cuts: messaging_product and recipient_type are gone (implied by the endpoint and the to field), and the phone number carries its + prefix — no regex cleanup code on your side.

Template message

Template sends are a straight swap — keeping Meta’s component schema means your existing components builders copy over without a rewrite.

Media message

Meta forces a two-step (upload to their media endpoint → send the returned id). Orbit accepts a public URL directly; if you orchestrate signed/private content, upload to Orbit’s files API first (POST /api/v1/files/upload) and pass the returned data.url. The channel reference lists every type block — image, video, audio, document, sticker, location, contacts — with the fields each accepts.

Webhooks

Inbound message

The flatten is the part that deletes the most code: no entry[].changes[].value[] walker, no filtering for field === "messages", no timestamp-string-to-ISO conversion. Your handler reads type, switches on it, done.

Delivery lifecycle

Meta emits statuses entries inside the same envelope; Orbit emits one typed event per transition — message.sent, message.delivered, message.read, message.failed — each carrying the message_id you persisted when you sent. The full event catalogue and payload contracts are the webhook events reference.

Templates

After you migrate a WABA from another BSP, run the sync once so the dashboard template picker picks up what Meta already approved:
Runtime selection is by (name, language) — the same pair Meta uses — so code that names templates works identically before and after.

Session windows → window status and fallbacks

Meta’s Cloud API rejects a free-form send outside the 24-hour window with 400 and an unstructured reason string. Orbit rejects it with 422 and a typed error body, and gives you the pre-flight:
That one GET is what turns “retry the template branch after every failure” into “compose the right message type first”. The model is documented in the 24-hour window.

Error surface

Migration checklist

  • Swap auth header Authorization: Bearer → X-API-Key; drop the token-rotation cron
  • Replace /{phone-number-id}/messages URL builder with the single messages endpoint
  • Move every messaging_product / recipient_type literal out of payloads
  • Accept +-prefixed numbers end to end (retire the strip-the-plus normalizer)
  • Replace the webhook verify GET handler with X-Orbit-Signature verification
  • Replace the nested webhook walker with a type switch on message.* / whatsapp.template.*
  • Swap Meta media-upload-then-send flows for URL-first sends
  • Subscribe to whatsapp.template.approved / .rejected and delete the template-status poller
  • Run the getting-started walkthrough end to end against a dev key before cutting production keys

See also