{
"id": "evt_01JB8ZE5N7Q9S1V3X5Z7B9D1E3",
"created_at": "2023-11-07T05:31:56Z",
"app": "app_01JB8Z0A1C3E5G7J9K1M3P5R7T",
"livemode": true,
"timing": {
"received_at": "2023-11-07T05:31:56Z",
"stored_at": "2023-11-07T05:31:56Z",
"delivered_at": "2023-11-07T05:31:56Z"
},
"type": "message.received",
"data": {
"message": {
"id": "msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2",
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"content": {
"type": "text",
"text": "<string>",
"format": "plain"
},
"livemode": true,
"created_at": "2023-11-07T05:31:56Z",
"channel_message_id": "<string>",
"metadata": {},
"updated_at": "2023-11-07T05:31:56Z"
}
}
}{
"reply": {
"type": "text",
"text": "Yes, we deliver to 560103."
}
}An event delivered to your endpoint
Flow POSTs each event your endpoint subscribes to, one event per request,
in order per conversation. Verify Flow-Signature before trusting the body
(see “Webhooks” in the introduction). Deliveries are at least once:
deduplicate on the event’s id.
To reply at once to a message.received event, answer 200 with a
WebhookReply body; the reply goes into the event’s conversation through the
send gate, as if you had called POST /v1/conversations/{conversation_id}/messages
with the event’s id as the idempotency key. fallback applies to every
piece of the reply. For any other event, or to reply later, answer 200 with
an empty body, {}, {"reply": null} or any body that is not a JSON object
with reply (plain text such as OK included): nothing is sent and it is not
an error.
An answer to a message.received delivery that is a JSON object with a
reply that is not valid (a reply that is not content or a list of
content, an empty list, more than 10 pieces, an unknown fallback), or a
body that starts with { but is not valid JSON, sends nothing at all, not
even the valid pieces. It is recorded
on the delivery as an invalid_request error with the reason, which the MCP
tool get_webhook_deliveries shows; the delivery counts as delivered and is
not retried. A piece that the send gate refuses is reported as a
message.failed event.
{
"id": "evt_01JB8ZE5N7Q9S1V3X5Z7B9D1E3",
"created_at": "2023-11-07T05:31:56Z",
"app": "app_01JB8Z0A1C3E5G7J9K1M3P5R7T",
"livemode": true,
"timing": {
"received_at": "2023-11-07T05:31:56Z",
"stored_at": "2023-11-07T05:31:56Z",
"delivered_at": "2023-11-07T05:31:56Z"
},
"type": "message.received",
"data": {
"message": {
"id": "msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2",
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"content": {
"type": "text",
"text": "<string>",
"format": "plain"
},
"livemode": true,
"created_at": "2023-11-07T05:31:56Z",
"channel_message_id": "<string>",
"metadata": {},
"updated_at": "2023-11-07T05:31:56Z"
}
}
}{
"reply": {
"type": "text",
"text": "Yes, we deliver to 560103."
}
}Authorizations
An API key of one app, sent as Authorization: Bearer <key>. Keys start with
fk_test_ (test mode: sandbox senders and test data only) or fk_live_
(live mode). Keep live keys on your server; never ship them in an app or page.
Headers
t=<unix seconds>,v1=<hex HMAC-SHA256 of "t.{t}.{body}">, with one v1 per active signing secret.
"t=1791763200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
The event's id, also in the body. Use it to deduplicate.
An event ID, evt_ and a ULID.
^evt_[0-9A-HJKMNP-TV-Z]{26}$"evt_01JB8ZE5N7Q9S1V3X5Z7B9D1E3"
The event's type, also in the body.
message.received: the contact sent something with content (a button tap arrives asbutton_replycontent).message.sent,message.delivered,message.read,message.failed: the status of your outbound messages.message.failedcarries the error indata.message.error.reaction.added,reaction.removed: the contact reacted to a message.typing.started,typing.stopped: the contact is typing, where the channel reports it.conversation.started: the first inbound message from a new contact, or a sandbox join (Flow itself answers the join; the join message is not amessage.received).conversation.window_closing: WhatsApp only, opt-in. The 24-hour window closes in 1 hour.sender.status_changed: a sender was throttled, flagged, banned or restored, or its WhatsApp quality rating changed.template.status_changed: Meta approved, rejected or paused a template.
message.received, message.sent, message.delivered, message.read, message.failed, reaction.added, reaction.removed, typing.started, typing.stopped, conversation.started, conversation.window_closing, sender.status_changed, template.status_changed The API version the event body is shaped by (your app's pinned version).
Body
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
- Option 6
- Option 7
- Option 8
- Option 9
- Option 10
- Option 11
- Option 12
- Option 13
One entry in your app's log. type says what happened and selects the shape
of data. conversation is set for every event that happened in a
conversation (all but sender.status_changed and template.status_changed).
An event ID, evt_ and a ULID.
^evt_[0-9A-HJKMNP-TV-Z]{26}$"evt_01JB8ZE5N7Q9S1V3X5Z7B9D1E3"
When the event was created. Events are ordered in the log by commit, not by this time.
An app ID, app_ and a ULID.
^app_[0-9A-HJKMNP-TV-Z]{26}$"app_01JB8Z0A1C3E5G7J9K1M3P5R7T"
Whether the event belongs to live mode.
When the event moved through Flow, to measure Flow's overhead
(delivered_at minus received_at).
Show child attributes
Show child attributes
Always message.received.
message.received The message the event is about.
Show child attributes
Show child attributes
The conversation an event happened in, inlined so most handlers need no extra call.
Show child attributes
Show child attributes
Response
The event was received. Optionally carries a reply to send into the event's conversation.
The optional body of your answer to a message.received delivery. reply is
one piece of content or a list of up to 10, sent in order into the event's
conversation through the send gate. A non-empty string, as reply or as an
item of the list, is text: {"reply": "Hi"} is short for
{"reply": {"type": "text", "text": "Hi"}}. Leave reply out, set it to null, or
answer {}, an empty body or any body that is not a JSON object (such as
OK) to send nothing now; that is not an error.
A JSON object whose reply (or fallback, alongside a reply) does not match
this shape, or a body that starts with { but is not valid JSON, sends
nothing at all (no piece of a list goes out) and is recorded on the delivery as an invalid_request error
with the reason; the delivery counts as delivered and is not retried.
One piece of content, or a list of 1 to 10 pieces sent in order. A non-empty string is text content. null sends nothing.
Show child attributes
Show child attributes
What to send when the channel cannot show a piece of reply, as on HTTP sends. It applies to every piece.
auto