Skip to main content
WhatsApp Flows are Meta-hosted interactive forms (screens of inputs and navigation) opened from a chat. LazyApp stores a workspace mirror of each flow, publishes to Meta, and records completions on webhooks and GET /v1/flows/{flow}/responses. Do not confuse Flows with workflows (LazyApp automation graphs) or templates (approved outbound message shells). A Flow can be attached to a template button or sent as an interactive message inside the 24-hour window.
WhatsApp Flows are on the Growth and Pro plans. On a smaller plan you can still open and delete existing Flows, but not create, change or publish them, or send them from the inbox. Test mode is never limited. See Plans and limits.
Coexistence numbers report flows: false and flows.data_endpoint: false on GET /v1/capabilities. Build Flow onboarding for Cloud-API-only numbers. See WhatsApp Business app coexistence.

Check before you build

Required scope for management APIs: flows:write (list, create, publish, deprecate, responses, endpoint health). Sending a Flow message also needs messages:send.

What LazyApp supports

Lifecycle

Meta may also mark a flow blocked or throttled. Only published and throttled are sendable. Published flows cannot be edited in place — duplicate into a new draft, then publish.

Starters

Duplicate

Create a flow

Invalid Flow JSON returns 422 with field errors before Meta is called. On success the flow is created at Meta and stored as draft with a provider_flow_id.
Console starters (lead_capture, appointment_booking, and others) produce the same draft shape without writing Flow JSON by hand.

Publish and deprecate

Publishing emits flow.published (or flow.failed on Meta error). Deprecate with POST …/deprecate when you want to stop attaching the flow to new messages. If Meta later throttles or blocks the flow because its endpoint is failing, you get flow.status_changed.

Update or delete a draft

PUT /v1/flows/{flow} takes the same body as create and replaces the draft. A published flow answers 422 with code: flow_not_editable. DELETE /v1/flows/{flow} deletes a draft at Meta and in LazyApp, and answers 204. A published flow answers 422 with code: flow_not_deletable: deprecate it instead.

Can it be sent?

Sending a flow depends on the flow, your WhatsApp Business account, your Meta business and LazyApp’s app. Meta rates each one and LazyApp asks for your number specifically:
can_send_message is AVAILABLE, LIMITED (sendable, see notes) or BLOCKED (see problems). The result is kept on the flow as can_send_message. In the console, use Check it can be sent on a flow.

Copy flows from another number

If your workspace has numbers on two WhatsApp Business accounts owned by the same Meta business, copy the flows from one into the workspace’s main account:
Leave out flow_names to copy every flow (Meta copies up to 100 per request). Copies keep their names and status; a name that already exists is reported under failed with Meta’s reason. In the console: Flows → ⋯ → Copy flows from another number.

Data endpoint

Meta calls a data endpoint for flows that load data while the customer fills them in. That traffic is encrypted with a key pair registered on your number. LazyApp is always the endpoint Meta calls: it keeps the key, decrypts each request and either answers from the Flow JSON (flows without a data_endpoint_uri) or forwards plain JSON to your data_endpoint_uri, signed with X-LazyApp-Signature: sha256=… using the flow’s signing secret. Your reply is encrypted back for Meta. Set up the number’s key once under Flows → Secure flow connection.

Verify the signature

POST /v1/flows returns the flow’s endpoint_secret (whfs_…) when you set a data_endpoint_uri, and so does the PUT that first adds one. It is shown only then, like a webhook secret, so store it with your endpoint’s config. Lost it, or need to rotate? POST /v1/flows/{flow}/rotate-endpoint-secret returns a new one; the old one stops verifying at once.
Check every forwarded request before trusting it: compute HMAC-SHA256 of the raw body with the secret and compare it, in constant time, with the hex after sha256= in X-LazyApp-Signature.

Data endpoint health

Probe it the way Meta does:
LazyApp sends an encrypted, signed ping to its own hosted endpoint, exactly as Meta does, and records the result on the flow.

Send a Flow message

Inside the customer-service window (or via a template that opens the Flow):
Use Meta’s provider_flow_id as flow_id. Workflows can launch published flows with a generated flow_token via the launch_flow node.

Collect responses

When the contact finishes, Meta delivers an interactive nfm_reply. LazyApp:
  1. Stores a WhatsAppFlowResponse linked to the flow
  2. Emits flow.response_received
  3. Exposes the payload on the list API
Prefer webhooks for live capture; list + console CSV export are for backfill and ops.

Provisioning with automation packs

PUT /v1/automation-packs/provision can seed starter Flows for a vertical (include_flows: true) alongside events and workflows — useful for platform customers.

What we skip (Zernio-style)