WhatsApp Flows and form submissions
A WhatsApp Flow is a multi-screen form Meta renders natively inside WhatsApp. A completed Flow returns one structured reply instead of a free-text back-and-forth, and Orbit persists that reply as a submission you can list, join to a contact, and react to. This page explains the model; the build-and-send walkthrough is WhatsApp Flows: interactive forms end-to-end, and the field-level read surface is on the WhatsApp channel page.What a WhatsApp Flow is
A Flow is a JSON document of screens, components (text inputs, dropdowns, radio groups, date pickers, images), and the routing between screens, hosted on Meta’s infrastructure. The recipient taps a CTA button on an interactive message — “Book an appointment”, “Complete this survey” — and the WhatsApp client renders the screens in-thread, with no browser hand-off. When the recipient completes the final screen, the form’s answers return as one structured payload rather than a typed message. Two properties matter for modeling:- The Flow lives at Meta. Orbit authors and uploads the JSON, but Meta hosts it, validates it at upload and publish time, and renders it. Publishing is irreversible on Meta’s side; lifecycle state on the Orbit side mirrors Meta’s status (
DRAFT→PUBLISHED, withDEPRECATED/BLOCKED/THROTTLEDas failure states). - The receipt is a template-scoped object too. Orbit stores the Flow it submitted against — the name, the uploaded JSON, and the Meta flow id — so a submission always joins back to the exact form version that produced it.
The submissions pipeline
A Flow returns a submission over one of two paths, and Orbit handles both:- In-conversation reply (
nfm_reply). The completed form arrives as an inboundmessage.receivedwebhook event with aninteractivepayload of typenfm_reply. Itsresponse_jsonholds the answers; theflow_tokenyou set on the send echoes back so you can join the submission to the session you opened. - Meta endpoint exchange. For Flows bound to a Meta Flow Endpoint, a Flow’s data-exchange and completion actions POST (encrypted) to Orbit’s Flow endpoint route. Orbit decrypts the request, persists the submission, and replies with the encrypted response Meta’s health check expects.
message.received for the in-conversation path; the endpoint path is server-to-server and writes the same rows.
Before either path writes anything, the inbound payload has to be resolved to its owning tenant — the same org→tenant mapping the inbound message resolution model describes for the conversational side. After resolution the submission is recorded in that tenant’s schema and fans out:
- Submission row — every completed submission persists with its raw payload, the resolved contact (or
nullwhen the submitter’s number matches no contact), the Meta conversation id, and the timestamps. TheGET /api/v1/whatsapp/flows/{flowId}/submissionsendpoint lists them newest-first. - Custom-field write-back — answers whose field ids match a defined custom field land on the contact’s profile, additively: blank answers never wipe a prior value, unmatched field ids are skipped, and one invalid answer never blocks the rest.
- CDP event — each completion emits one
whatsapp_flow_submittedevent onto the contact’s CDP profile. Meta retries of the same submission collapse onto one event, and the submissions list exposes thelanded_in_cdpmarker so you can confirm the event exists.
nfm_reply reply enters the same inbound pipeline as any received message — it persists to the conversation and fires the tenant-wide received event the routing engine evaluates. A rule that matches the reply’s sender or number can steer that conversation to an inbox, queue, team, or webhook; the Flow-specific persistence (submission row, write-back, CDP event) happens independently of which target the rule sends the conversation to. Endpoints of the Meta-flow-endpoint path never match routing rules — they are server-to-server writes, not inbound messages.
Failure triage
Submissions problems cluster into three failure classes, each with a dedicated playbook:- Dispatch and ingestion failures — a send that returned success but no submission rows, rows stuck with
landed_in_cdp: false, a funnel that shows completions but a list that stays empty, a dashboard panel that disagrees with the API. These are covered end-to-end on Troubleshoot: WhatsApp Flow submissions, including the pagination and window defaults that cause most dashboard-versus-API disagreements. - Tenant-resolution and contact-resolution failures — a submission whose submitter’s phone resolves to no contact lands with
contact_id: nulland never fires the CDP event until the contact exists. The contact-resolution branch is the second section of Troubleshoot: WhatsApp Flow submissions; the tenant-resolution model underneath is inbound message resolution. - Flow-runtime failures — when a recipient never completes the form at all (a blocked screen, an aborted encrypted exchange, a Flow stuck in
DRAFT), the story is on Flow execution failures, not here: a runtime failure produces no submission.
Composition with templates
A Flow is a template-shaped object: you author it once, Meta reviews (validates) it, and it moves through a lifecycle (DRAFT → PUBLISHED) before sends can reference it. The general model behind create → review → sendable → archive is Template lifecycle and approval semantics — Flows apply the same pattern to structured form content instead of message bodies, with Meta’s per-Flow validation errors substituting for the carrier review queue. One structural difference is worth knowing: a Flow’s definition carries screens and routing in addition to copy, so its JSON is the artifact, and the Visual Flow Builder plus the lifecycle endpoints are its editing surface.
The same is true on the send path: outside the 24-hour window only an approved template can open the conversation, and a Flow rides as an interactive message once the window is open — the trade-off table on the Flows guide covers when to pick a Flow versus a template versus a catalog message.
See also
- WhatsApp Flows: interactive forms end-to-end — the build, publish, send, and read walkthrough.
- WhatsApp channel — the Flow builder, the submissions read surface, and the custom-field write-back rules.
- Troubleshoot: WhatsApp Flow submissions — the dispatch/resolution/ingestion playbook this page points at.
- Inbound message resolution — the tenant-resolution model the pipeline depends on.
- Inbound message routing — how an inbound
nfm_replyreply composes with routing rules. - Template lifecycle and approval semantics — the lifecycle model Flows specialize.
- The CDP event model — what a
whatsapp_flow_submittedevent on a contact drives.