Skip to main content

Flows

Flows: create one from Flow JSON or an XLSForm, read it with its derived status, the groups that can reach it, its record action and the published Flow JSON, edit its settings, and delete it.

📄️Create a flow

Creates the flow and its first version, from Flow JSON (`application/json`) or from an uploaded XLSForm or FHIR Questionnaire (`multipart/form-data`, a `source_form` file part beside the metadata fields). Never both. A source form is converted before this responds; its problems are immediate: 422 `conversion_failed` with the converter's sentence, 504 `converter_timeout`, 502 `converter_unavailable`. The response is 202: the flow exists and its first version is `pending` while WhatsApp processes it. Poll `GET /v1/flows/{flow_id}/versions/{version_id}` with `latest_version.id` until `state` leaves `pending`; a rejected document is `failed` with `failure_reason`, an accepted one `draft` with a `preview_url`, and `publishable` says when it can be published. `warnings` lists what the converter dropped or degraded; it is empty for Flow JSON. `number_id` defaults to your organisation's number (422 `no_number_configured` when it has none) and must be one of yours. `record_action` is not accepted here; set it through PUT /v1/flows/{flow_id}/record-action once the version has left `pending`, then publish. Bodies up to 5 MB. `Idempotency-Key` is accepted but not required: send one and a retry of the same request replays the first response as 200 with `Idempotent-Replayed: true` instead of creating a second flow. Requires the `flows:write` scope.

📄️Update a flow

Changes the flow's settings: name, description, confirmation message, visibility and notify contacts. Omitted keys keep their value. Ownership, status and versions are never writable here, and the record action has its own endpoint. Making a private flow public claims its keywords across Hop; 409 `code_conflict` when one is already taken. It also opens the flow to anyone: a public flow accepts a submission from any phone number that texts its keyword and creates the contact on first use, where a private flow accepts only contacts your organisation already has. Requires the `flows:write` scope.

📄️Remove the flow's record action

Future completions stop writing records. Existing records and submissions are never touched by this call. The flow's own backing register goes with the action only when the action provisioned it, it holds no record, and nothing else uses it; a register with records, one that belongs to another flow, or one another flow's action writes into stays. Idempotent: an absent action is 204 too. 409 `register_in_use` when an option list of some flow still reads the register that would go; the detail says which flow, or that it is this flow's own version. Requires `flows:write`.

📄️Create or replace the flow's record action

The API face of the editor's Records card: what completing this flow does to a register. A full replacement; 201 when the flow had no action, 200 when it had one. Omitting `register_id` uses the flow's own backing register, created and named after the flow the first time and reused after that. `record_id_key` must be an option list of the flow's current version bound to that register; `label_key` and every `excluded_keys` entry must be fields of that version (any named component, including pickers). `update` or `create_or_update` with a null `record_id_key` is allowed, and means only sends started from a record can aim the flow. Field-referencing settings need a version WhatsApp has processed: while the flow's version is still `pending` they are 422 `validation_failed` (`no_processed_version`); a field-free action (`mode` alone) is accepted at any time. Other `errors[]` codes: `unknown`, `not_register_bound`, `not_applicable` (a `record_id_key` with `create`), `not_a_flow_register` (a CSV register). The current version is the published one when there is one, else the newest. No `Idempotency-Key`: the request is a full replace, so retrying after a lost response lands on the same state; the only difference a retry sees is 200 where the first attempt would have said 201. Requires `flows:write`.