Messaging

Flows

Interactive WhatsApp forms hosted by Meta. Store the JSON definition with Tyxter, publish to Meta, and receive every end-user submission as a signed webhook.

Create

POST/v1/flows
curl https://api.tyxter.com/v1/flows \
  -H "authorization: Bearer $TYXTER_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "name": "lead_capture",
    "flow_json": {
      "version": "6.3",
      "screens": [
        {
          "id": "WELCOME",
          "title": "Welcome",
          "terminal": true,
          "layout": { "type": "SingleColumnLayout", "children": [] }
        }
      ]
    }
  }'

We validate the JSON structurally before storage — unsupported versions, missing screens, missing terminal screens, and duplicate screen ids are rejected with 400 invalid_flow_json. Meta will do its own semantic validation at publish time.

Send

POST/v1/messages

Send a published flow to a recipient with the channel-native messages route, using message.type = "flow". Address the flow with flow.action.parameters.flow_id — the published flow's provider_flow_id, which you read from GET /v1/flows/{id} once publish completes — or with flow.action.parameters.flow_name; provide exactly one. flow_token is your required opaque correlation token: it comes back to you as submission.provider_token on the flow.completed webhook, so use it to tie a completion back to your own record. flow_cta is the required button label (max 30 chars).

curl https://api.tyxter.com/v1/messages \
  -H "authorization: Bearer $TYXTER_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "channel": "whatsapp",
    "sender": { "type": "whatsapp_phone_number", "id": "pn_..." },
    "recipient": { "type": "phone_e164", "id": "+5511999999999" },
    "message": {
      "type": "flow",
      "flow": {
        "body": { "text": "Tap below to complete your booking." },
        "action": {
          "name": "flow",
          "parameters": {
            "flow_id": "1234567890",
            "flow_token": "booking-7f3a",
            "flow_cta": "Book now"
          }
        }
      }
    }
  }'

Publish

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

Snapshots the current flow_json into a new FlowVersion and enqueues the Meta sync worker. Returns 202.

Sandbox auto-publishes without hitting Meta so you can build the submission → webhook loop locally.

Completions

When an end-user submits the flow, Meta posts the completion to our webhook receiver. We persist a FlowSubmission (idempotent onflow_token) and emit flow.completed for your customer webhook endpoints.

{
  "id": "evt_...",
  "type": "flow.completed",
  "data": {
    "flow_id": "flw_...",
    "name": "lead_capture",
    "status": "published",
    "provider_flow_id": "1234567890",
    "rejection_reason": null,
    "submission": {
      "id": "sub_...",
      "provider_token": "flow-token-xyz",
      "response": { "email": "user_at_example_dot_com", "size": "M" },
      "message_id": "msg_...",
      "received_at": "2026-04-27T12:05:00.000Z"
    }
  }
}

Every flow.completed data carries the correlation fields you need to tie a submission back: flow_id (the Tyxter flw_... id), provider_flow_id (the Meta flow id the message was sent with), submission.id, submission.provider_token (the flow_token you sent), submission.response (the submitted fields), and submission.message_id.

Lifecycle events