WhatsApp Flows

whatsapp.flows.* events are special: Platica not only notifies you that something happened, it also expects your response to continue the WhatsApp flow.

Use them when you want your backend to drive dynamic screens, validations, dropdown options, prices, confirmations, or any data that must be computed in real time.

Events

EventWhen it happens
whatsapp.flows.initThe user opens the flow (action: "INIT")
whatsapp.flows.screen_advanceThe user moves to the next screen (action: "data_exchange")
whatsapp.flows.backThe user returns to the previous screen (action: "BACK")
whatsapp.flows.exchangesUmbrella subscription to receive all three above on a single webhook

Delivery cycle

Payload received

{
  "id": "8f2c4d6a-1b3e-4f5d-8a9c-2e7b6d1a3f4e",
  "event": "whatsapp.flows.screen_advance",
  "workspaceId": "ws_456",
  "timestamp": "2026-05-06T19:00:30.000Z",
  "source": "whatsapp.flows",
  "resourceType": "flow_response",
  "resourceId": "ZmxvdyQwMQ==",
  "changes": null,
  "data": {
    "client": {
      "id": "client_123",
      "phoneNumber": "+5215555555555",
      "creationDate": "2026-05-01T12:00:00.000Z",
      "lastUpdate": "2026-05-06T18:59:00.000Z",
      "name": "Juan Pérez",
      "firstname": "Juan",
      "email": "juan@empresa.com"
    },
    "conversation": {
      "id": "conv_123",
      "conversationId": "conv_123",
      "canSendDirectMessage": true,
      "workspaceId": "ws_456",
      "channelId": "channel_wa_123",
      "contactName": "Juan Pérez",
      "phoneNumber": "+5215555555555",
      "topic": "",
      "platform": "whatsapp",
      "owners": [],
      "tags": [],
      "creationDate": "2026-05-06T18:58:00.000Z",
      "lastUpdate": "2026-05-06T19:00:00.000Z",
      "status": "open",
      "operation": "automatic",
      "messageCount": 3
    },
    "flow": {
      "id": "flow_doc_abc",
      "wabaId": "1234567890",
      "name": "Quote flow",
      "status": "PUBLISHED",
      "categories": ["LEAD_GENERATION"],
      "dataApiVersion": "3.0",
      "endpointUri": "https://app.platica.mx/api/whatsapp/flows/1234567890/data",
      "jsonVersion": "7.1",
      "creationDate": "2026-05-01T12:00:00.000Z",
      "lastUpdate": "2026-05-06T18:55:00.000Z",
      "lastSyncAt": "2026-05-06T18:55:00.000Z"
    },
    "flowResponse": {
      "flowToken": "ZmxvdyQwMQ==",
      "status": "IN_PROGRESS",
      "sentAt": "2026-05-06T18:59:00.000Z",
      "openedAt": "2026-05-06T19:00:00.000Z",
      "lastActivityAt": "2026-05-06T19:00:30.000Z",
      "completedAt": null,
      "expiresAt": "2026-05-07T18:59:00.000Z",
      "clientId": "client_123",
      "conversationId": "conv_123",
      "messageId": "wamid.HBgN...",
      "campaignId": null,
      "agentId": null,
      "channelId": "channel_wa_123"
    },
    "flowExchange": {
      "action": "data_exchange",
      "screen": "WELCOME",
      "version": "3.0",
      "payload": {
        "selected_product": "p2",
        "email": "juan@empresa.com"
      }
    }
  }
}
FieldTypeDescription
eventstringEvent received
idstringDelivery UUID
workspaceIdstringOwning workspace
timestampstringISO-8601 delivery timestamp
sourcewhatsapp.flowsStable event source
resourceTypeflow_responseType of affected resource
resourceIdstring \| nullMeta flow_token when present
changesnullReserved for compatibility with the common envelope
data.clientobject \| nullAssociated customer summary, if Platica could resolve it
data.conversationobject \| nullAssociated conversation, if there is a conversationId
data.flowobjectNormalized flow snapshot
data.flowResponseobject \| nullFlow response/session state
data.flowExchangeobjectExchange data received from Meta

data.flowExchange fields

FieldTypeDescription
actioninit \| data_exchange \| backMeta action normalized to lowercase
screenstring \| nullCurrent screen sent by Meta
versionstring \| nullWhatsApp Flows protocol version
payloadobjectData captured or sent by Meta for this step

Expected response

Your endpoint must respond with 2xx and JSON. Platica accepts the response if it meets at least one of the following:

  • It has data, and data is an object.
  • It has screen, and screen is a non-empty string.

Example for populating dynamic data:

{
  "data": {
    "products": [
      { "id": "p1", "title": "Plan Básico" },
      { "id": "p2", "title": "Plan Premium" }
    ]
  }
}

Example to advance to a screen:

{
  "screen": "PAYMENT",
  "data": {
    "amount_mxn": 499
  }
}

Example to show an error:

{
  "data": {
    "error_message": "El código postal no es válido"
  }
}

Headers and signature

HeaderDescription
Content-Typeapplication/json
User-AgentPlatica-Webhooks/1.0
X-Webhook-EventMatches payload.event
X-Webhook-IdIdentifier of the configured webhook
X-Webhook-Event-IdMatches payload.id
X-Webhook-Resource-Typeflow_response
X-Webhook-Resource-IdMatches payload.resourceId
X-Webhook-TimestampMatches payload.timestamp
X-Webhook-Signaturesha256=<hex> if you configured secret

The signature is computed with HMAC-SHA256 over the raw body:

sha256=<hmac_sha256(secret, rawBody)>

Node.js example:

const crypto = require('crypto');

function verifyFlowsWebhook(secret, rawBody, signatureHeader) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  );
}

whatsapp.flows.init

Happens when the user opens the flow. Usually used to preload initial data.

{
  "id": "5e3a1b9c-7d12-4a8e-9c5f-1e6b8d0a2c4f",
  "event": "whatsapp.flows.init",
  "workspaceId": "ws_456",
  "timestamp": "2026-05-06T19:00:00.000Z",
  "source": "whatsapp.flows",
  "resourceType": "flow_response",
  "resourceId": "ZmxvdyQwMQ==",
  "changes": null,
  "data": {
    "client": null,
    "conversation": null,
    "flow": {
      "id": "flow_doc_abc",
      "wabaId": "1234567890"
    },
    "flowResponse": {
      "flowToken": "ZmxvdyQwMQ==",
      "status": "IN_PROGRESS"
    },
    "flowExchange": {
      "action": "init",
      "screen": null,
      "version": "3.0",
      "payload": {}
    }
  }
}

whatsapp.flows.screen_advance

Happens when the user fills out a screen and taps continue. data carries the captured values.

{
  "id": "8f2c4d6a-1b3e-4f5d-8a9c-2e7b6d1a3f4e",
  "event": "whatsapp.flows.screen_advance",
  "workspaceId": "ws_456",
  "timestamp": "2026-05-06T19:00:30.000Z",
  "source": "whatsapp.flows",
  "resourceType": "flow_response",
  "resourceId": "ZmxvdyQwMQ==",
  "changes": null,
  "data": {
    "client": null,
    "conversation": null,
    "flow": {
      "id": "flow_doc_abc",
      "wabaId": "1234567890"
    },
    "flowResponse": {
      "flowToken": "ZmxvdyQwMQ==",
      "status": "IN_PROGRESS"
    },
    "flowExchange": {
      "action": "data_exchange",
      "screen": "WELCOME",
      "version": "3.0",
      "payload": {
        "selected_product": "p2",
        "email": "juan@empresa.com"
      }
    }
  }
}

whatsapp.flows.back

Happens when the user taps back. Use it to restore the data of a previous screen.

{
  "id": "3a5b7c9d-2e4f-4a6b-8c1d-5e7f9a2b4c6d",
  "event": "whatsapp.flows.back",
  "workspaceId": "ws_456",
  "timestamp": "2026-05-06T19:01:00.000Z",
  "source": "whatsapp.flows",
  "resourceType": "flow_response",
  "resourceId": "ZmxvdyQwMQ==",
  "changes": null,
  "data": {
    "client": null,
    "conversation": null,
    "flow": {
      "id": "flow_doc_abc",
      "wabaId": "1234567890"
    },
    "flowResponse": {
      "flowToken": "ZmxvdyQwMQ==",
      "status": "IN_PROGRESS"
    },
    "flowExchange": {
      "action": "back",
      "screen": "PAYMENT",
      "version": "3.0",
      "payload": {}
    }
  }
}

whatsapp.flows.exchanges

An umbrella subscription to receive init, screen_advance, and back on a single webhook.

Use whatsapp.flows.exchanges if your backend wants to handle every flow step with the same endpoint and the same logic.

If you need separate logic per step, subscribe to the granular events:

  • whatsapp.flows.init
  • whatsapp.flows.screen_advance
  • whatsapp.flows.back