Skip to content
Get an API key
Reference

Flows

22 endpoints in this family.

List Flow responses

GET/api/v1/flow-responses

Returns what contacts filled in on WhatsApp Flows, most recent first. Read from the received messages, so kept as long as message content is (see your plan's retention): store what you need through the message.received webhook (data.reply). Use flowId for the answers to one Flow, flowToken for the answer to one particular send.

Query parameters

  • flowIdstring

    Only the answers to this Flow (its Nodium id), whether it was sent as a flow form or opened from a template button. An unknown Flow answers 404.

  • flowTokenstring

    Only the answers to sends made with this flowToken.

  • fromstring

    Only answers at or after this instant (ISO 8601).

  • tostring

    Only answers before this instant (ISO 8601).

  • limitinteger

    How many to return.

  • cursorstring

    Opaque cursor from the previous page.

Response

A page of Flow answers.

  • data.itemsobject[]

    Each: messageId, conversationId, contact (id, name, identity), flowToken, data, createdAt.

  • data.nextCursorstring

    Pass it as cursor for the next page; null at the end.

  • data.hasMoreboolean

    Whether another page exists.

  • data.totalinteger

    How many match, on the first page only; null after.

curl https://nodium.io/api/v1/flow-responses \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "items": [ {} ], "nextCursor": "…", "hasMore": true, "total": 0 }}

List WhatsApp Flows

GET/api/v1/flows

Returns the WhatsApp Flows (interactive forms) of the client you act on, most recently changed first. A Flow is static until you attach a data endpoint, then dynamic. To send one, use its metaFlowId in a flow form (POST /messages, type: "interactive"). To pull in Flows drawn directly at Meta, call POST /flows/sync first.

Query parameters

  • statusstring

    Only Flows in this state.

    draftpublisheddeprecatedblockedthrottled

  • modestring

    Only static or only dynamic Flows.

    staticdynamic

  • qstring

    Search by name.

Response

The Flows of the client.

  • data.itemsobject[]

    The Flows, most recently changed first.

    Show fields
    • idstring

      The Nodium id.

    • metaFlowIdstring

      The id at Meta: the flowId of a flow form.

    • wabaIdstring

      The WhatsApp Business account it lives in.

    • namestring

      Its name.

    • statusstring

      draft, published (sendable, cannot change), deprecated, blocked or throttled (unhealthy endpoint).

      draftpublisheddeprecatedblockedthrottled

    • modestring

      dynamic once a data endpoint is attached.

      staticdynamic

    • categoriesstring[]

      Its Meta categories.

    • responsesinteger

      How many answers it has received, while message content is kept (GET /flow-responses?flowId=).

    • createdAtstring

      ISO 8601.

    • updatedAtstring

      ISO 8601.

curl https://nodium.io/api/v1/flows \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "items": [ { "id": "…", "metaFlowId": "…", "wabaId": "…", "name": "…", "status": "draft", "mode": "static", "categories": [], "responses": 0, "createdAt": "…", "updatedAt": "…" } ] }}

Create a WhatsApp Flow

POST/api/v1/flows

Creates a draft Flow at Meta, in a WhatsApp Business account of the client, and records its first version. Without flowJson the draft starts from a four-screen quote request. Meta validates the JSON: its errors come back in validationErrors and the draft exists anyway. The client needs at least one connected number; if it has several WhatsApp accounts, name one with wabaId.

Body

  • namestringrequired

    A name, 1 to 200 characters.

  • categoriesstring[]

    One or more of SIGN_UP, SIGN_IN, APPOINTMENT_BOOKING, LEAD_GENERATION, CONTACT_US, CUSTOMER_SUPPORT, SURVEY, OTHER. Default OTHER.

  • channelIdstring

    A connected number of the client: the Flow is created in its WhatsApp Business account.

  • wabaIdstring

    The WhatsApp Business account, when the client has several (ignored when channelId is given).

  • flowJsonobject

    The Flow JSON (10 MB at most). Default: a sample quote request.

Response

The draft Flow, its first version and Meta's validation errors.

  • data.flowobject

    The Flow (see GET /flows).

  • data.versionobject

    Its first version: label, sha, validationErrors, createdAt, flowJson.

  • data.validationErrorsobject[]

    What Meta found wrong in the JSON; empty when it is valid.

Refusals

  • 409no_channelThe client has no connected WhatsApp number.
  • 502upstream_errorMeta refused or did not answer.
curl -X POST https://nodium.io/api/v1/flows \ -H "Authorization: Bearer $NODIUM_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "…" }'

Response

200 OK
{ "data": { "flow": {}, "version": {}, "validationErrors": [ {} ] }}

Delete a draft Flow

DEL/api/v1/flows/{id}

Deletes a draft Flow at Meta and here, with its versions. A published Flow cannot be deleted: deprecate it instead.

Path parameters

  • idstring

    The Flow id.

Response

Confirmation that the draft is gone.

  • data.deletedboolean

    Always true.

Refusals

  • 409conflictThe Flow is published, deprecated, blocked or throttled: only a draft can be deleted.
  • 502upstream_errorMeta refused or did not answer.
curl -X DELETE https://nodium.io/api/v1/flows/<id> \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "deleted": true }}

Get a WhatsApp Flow

GET/api/v1/flows/{id}

Returns the Flow, its latest version with the JSON and Meta's validation errors, and its data endpoint when it is dynamic (the address Meta calls on Nodium, and yours). The signing secret is never returned.

Path parameters

  • idstring

    The Flow id.

Response

The Flow, its latest version and its data endpoint.

  • data.flowobject

    The Flow (see GET /flows).

  • data.versionobject

    The latest version (label, sha, flowJson, validationErrors, publishedAt, createdAt); null before the first.

  • data.dataEndpointobject

    url (yours) and metaUrl (the address Meta calls); null for a static Flow.

curl https://nodium.io/api/v1/flows/<id> \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "flow": {}, "version": {}, "dataEndpoint": {} }}

Rename a WhatsApp Flow

PATCH/api/v1/flows/{id}

Changes the name or the categories of a Flow, at Meta and here. Send only what changes. A deprecated Flow cannot be changed.

Path parameters

  • idstring

    The Flow id.

Body

  • namestring

    A new name, 1 to 200 characters.

  • categoriesstring[]

    One or more of SIGN_UP, SIGN_IN, APPOINTMENT_BOOKING, LEAD_GENERATION, CONTACT_US, CUSTOMER_SUPPORT, SURVEY, OTHER.

Response

The Flow as it now is.

  • data.flowobject

    The Flow (see GET /flows).

Refusals

  • 409conflictThe Flow is deprecated.
  • 502upstream_errorMeta refused or did not answer.
curl -X PATCH https://nodium.io/api/v1/flows/<id> \ -H "Authorization: Bearer $NODIUM_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "…", "categories": [ "…" ] }'

Response

200 OK
{ "data": { "flow": {} }}

Detach the data endpoint of a Flow

DEL/api/v1/flows/{id}/data-endpoint

Makes the Flow static again: Meta no longer calls an endpoint and Nodium stops relaying. Only a draft can change.

Path parameters

  • idstring

    The Flow id.

Response

Confirmation that the Flow is static.

  • data.removedboolean

    Always true.

Refusals

  • 409conflictThe Flow is not a draft.
  • 502upstream_errorMeta refused or did not answer.
curl -X DELETE https://nodium.io/api/v1/flows/<id>/data-endpoint \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "removed": true }}

Get the data endpoint of a Flow

GET/api/v1/flows/{id}/data-endpoint

Returns the data endpoint of a dynamic Flow: url (yours, where Nodium relays) and metaUrl (the address Meta calls on Nodium). null for a static Flow. The signing secret is never returned.

Path parameters

  • idstring

    The Flow id.

Response

The data endpoint, or null.

  • data.dataEndpointobject

    url and metaUrl; null when the Flow is static.

curl https://nodium.io/api/v1/flows/<id>/data-endpoint \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "dataEndpoint": {} }}

Attach a data endpoint to a Flow

PUT/api/v1/flows/{id}/data-endpoint

Makes the Flow dynamic. Meta calls Nodium on every screen; Nodium decrypts the request and relays it to your url as a signed POST { source, flow, data_exchange: { version, action, screen, data, flow_token }, signature_valid, received_at } (header X-Nodium-Signature, same scheme as webhooks); your JSON answer { screen, data } is encrypted and returned to Meta. You have about 9 seconds to answer. Nodium also generates the encryption key of the client and registers it on the client's numbers. The signing secret is returned once, here. Only a draft can change.

Path parameters

  • idstring

    The Flow id.

Body

  • urlstringrequired

    Your https address, up to 2000 characters.

Response

The data endpoint and its signing secret β€” shown only here.

  • data.dataEndpointobject

    url and metaUrl.

  • data.secretstring

    The signing secret, whsec_…. Shown only here.

  • data.unregisteredChannelsstring[]

    Numbers that did not take the encryption key; call this route again once they are fixed.

Refusals

  • 409conflictThe Flow is not a draft.
  • 422webhook_url_not_httpsThe address must start with https://.
  • 502upstream_errorMeta refused or did not answer.
curl -X PUT https://nodium.io/api/v1/flows/<id>/data-endpoint \ -H "Authorization: Bearer $NODIUM_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "…" }'

Response

200 OK
{ "data": { "dataEndpoint": {}, "secret": "…", "unregisteredChannels": [ "…" ] }}

Try the data endpoint of a Flow

POST/api/v1/flows/{id}/data-endpoint-test

Sends your address a signed INIT call (the one Meta makes when the form opens), marked test: true, and returns the HTTP status, the duration and your answer (cut at 4,000 characters). It does not go through Meta and is not listed in the calls of the Flow.

Path parameters

  • idstring

    The Flow id.

Response

What your address answered.

  • data.okboolean

    A 2xx answer whose body is a JSON object.

  • data.statusinteger

    The HTTP status; null when nothing answered.

  • data.durationMsinteger

    How long it took.

  • data.bodystring

    Your answer, or the reason it failed.

Refusals

  • 409conflictThe Flow has no data endpoint.
curl -X POST https://nodium.io/api/v1/flows/<id>/data-endpoint-test \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "ok": true, "status": 0, "durationMs": 0, "body": "…" }}

Deprecate a Flow

POST/api/v1/flows/{id}/deprecate

Retires a published Flow: it can no longer be sent or opened. Messages already sent stay in the conversations. It cannot be undone.

Path parameters

  • idstring

    The Flow id.

Response

The deprecated Flow.

  • data.flowobject

    The Flow, now deprecated.

Refusals

  • 409conflictOnly a published Flow can be deprecated.
  • 502upstream_errorMeta refused or did not answer.
curl -X POST https://nodium.io/api/v1/flows/<id>/deprecate \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "flow": {} }}

Duplicate a Flow

POST/api/v1/flows/{id}/duplicate

Copies the Flow at Meta into a new draft in the same WhatsApp Business account, with the same JSON. This is how you change a published Flow. The data endpoint is not copied: attach it again on the copy.

Path parameters

  • idstring

    The Flow id.

Body

  • namestring

    A name for the copy. Default: the name followed by (copy).

Response

The new draft.

  • data.flowobject

    The new draft Flow (see GET /flows).

Refusals

  • 502upstream_errorMeta refused or did not answer.
curl -X POST https://nodium.io/api/v1/flows/<id>/duplicate \ -H "Authorization: Bearer $NODIUM_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "…" }'

Response

200 OK
{ "data": { "flow": {} }}

Get the sending health of a Flow

GET/api/v1/flows/{id}/health

Asks Meta whether the Flow can be sent right now (AVAILABLE, LIMITED or BLOCKED), and for each entity involved (the Flow, the WhatsApp Business account, the business, the app) what blocks it and how to fix it.

Path parameters

  • idstring

    The Flow id.

Response

What Meta says about sending this Flow.

  • data.canSendMessagestring

    AVAILABLE, LIMITED, BLOCKED, or null when Meta says nothing.

  • data.entitiesobject[]

    Each entity: type, id, canSendMessage, errors (code, description, solution).

Refusals

  • 502upstream_errorMeta refused or did not answer.
curl https://nodium.io/api/v1/flows/<id>/health \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "canSendMessage": "…", "entities": [ {} ] }}

List the calls Meta made to a Flow

GET/api/v1/flows/{id}/invocations

The last 100 calls Meta made to a dynamic Flow, most recent first: the action (INIT, data_exchange, BACK, ping), the screen, the HTTP status your endpoint answered, how long it took, and the error if any. The content exchanged is never kept. Calls are kept thirty days.

Path parameters

  • idstring

    The Flow id.

Response

The last calls.

  • data.itemsobject[]

    id, action, screen, statusCode, durationMs, error, createdAt.

curl https://nodium.io/api/v1/flows/<id>/invocations \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "items": [ {} ] }}

Get the preview of a Flow

GET/api/v1/flows/{id}/preview

Returns the address of the preview page Meta renders for the Flow β€” the form as it looks on a phone, with Android and iOS, light and dark modes to choose from. The address expires after about thirty days; ask again when it does.

Path parameters

  • idstring

    The Flow id.

Response

The preview address.

  • data.previewUrlstring

    The address to open or embed.

  • data.expiresAtstring

    When it stops working, ISO 8601, or null.

Refusals

  • 502upstream_errorMeta refused or did not answer.
curl https://nodium.io/api/v1/flows/<id>/preview \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "previewUrl": "…", "expiresAt": "…" }}

Publish a Flow

POST/api/v1/flows/{id}/publish

Publishes the draft so it can be sent to customers. This is final: a published Flow can no longer be changed or unpublished β€” duplicate it to change it, deprecate it to stop it. Meta refuses to publish JSON that still has validation errors, and says why.

Path parameters

  • idstring

    The Flow id.

Response

The published Flow.

  • data.flowobject

    The Flow, now published.

Refusals

  • 409conflictThe Flow is not a draft.
  • 502upstream_errorMeta refused (the JSON is invalid, for example) or did not answer.
curl -X POST https://nodium.io/api/v1/flows/<id>/publish \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "flow": {} }}

List the versions of a Flow

GET/api/v1/flows/{id}/versions

Every JSON saved for the Flow, most recent first: label (v1, v2…), sha, Meta's validationErrors, and publishedAt on the version Meta published. Versions are immutable. The JSON itself is left out: read one with GET /flows/{id}/versions/{versionId}.

Path parameters

  • idstring

    The Flow id.

Response

The versions of the Flow.

  • data.itemsobject[]

    The versions, most recent first, without flowJson.

curl https://nodium.io/api/v1/flows/<id>/versions \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "items": [ {} ] }}

Save a new version of a Flow

POST/api/v1/flows/{id}/versions

Sends the JSON to Meta, which validates it, and records it as a new version. Invalid JSON is saved too: validationErrors says what to fix before publishing. Saving the same JSON as the latest version does not open a new one. Only a draft can change; to change a published Flow, duplicate it first.

Path parameters

  • idstring

    The Flow id.

Body

  • flowJsonobjectrequired

    The Flow JSON (10 MB at most).

Response

The version and Meta's validation errors.

  • data.versionobject

    label, sha, flowJson, validationErrors, createdAt.

  • data.validationErrorsobject[]

    What Meta found wrong; empty when the JSON is valid.

Refusals

  • 409conflictThe Flow is not a draft.
  • 502upstream_errorMeta refused or did not answer.
curl -X POST https://nodium.io/api/v1/flows/<id>/versions \ -H "Authorization: Bearer $NODIUM_KEY" \ -H "Content-Type: application/json" \ -d '{ "flowJson": {} }'

Response

200 OK
{ "data": { "version": {}, "validationErrors": [ {} ] }}

Get one version of a Flow

GET/api/v1/flows/{id}/versions/{versionId}

Returns one saved version with its JSON and the validation errors Meta gave it.

Path parameters

  • idstring

    The Flow id.

  • versionIdstring

    The version id.

Response

The version.

  • data.versionobject

    label, sha, flowJson, validationErrors, publishedAt, createdAt.

curl https://nodium.io/api/v1/flows/<id>/versions/<versionId> \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "version": {} }}

Read the encryption state of the client's numbers

GET/api/v1/flows/encryption

Meta encrypts the calls to a dynamic Flow with a key registered per phone number. Returns each number of the client with state: ready (the client's key is registered), missing (to register with POST /flows/encryption) or unknown (Meta did not answer).

Response

The encryption state of each number.

  • data.itemsobject[]

    channelId, name, phone, state.

curl https://nodium.io/api/v1/flows/encryption \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "items": [ {} ] }}

Register the encryption key on a number

POST/api/v1/flows/encryption

Creates the client's encryption key if it has none, then registers its public part at Meta on one number (channelId) or on all of the client's numbers. It is a one-time step per number. Returns the state of each number.

Body

  • channelIdstring

    A number of the client. Omit to register on all of them.

Response

The encryption state of each number.

  • data.itemsobject[]

    channelId, name, phone, state.

Refusals

  • 404not_foundThe number is not one of the client's.
curl -X POST https://nodium.io/api/v1/flows/encryption \ -H "Authorization: Bearer $NODIUM_KEY" \ -H "Content-Type: application/json" \ -d '{ "channelId": "…" }'

Response

200 OK
{ "data": { "items": [ {} ] }}

Sync Flows from Meta

POST/api/v1/flows/sync

Reads the Flows of the client's WhatsApp Business accounts at Meta and records them here: Flows drawn directly in WhatsApp Manager show up, the others get their name, status and categories refreshed. Nothing is created or changed at Meta, and a Flow that no longer exists there is kept.

Response

The Flows of the client after the sync.

  • data.itemsobject[]

    The Flows of the client (see GET /flows).

  • data.syncedinteger

    How many Flows were read at Meta.

Refusals

  • 502upstream_errorMeta refused or did not answer.
curl -X POST https://nodium.io/api/v1/flows/sync \ -H "Authorization: Bearer $NODIUM_KEY"

Response

200 OK
{ "data": { "items": [ {} ], "synced": 0 }}