Skip to main content
WEBHOOK

Authorizations

Authorization
string
header
required

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

Flow-Signature
string
required

t=<unix seconds>,v1=<hex HMAC-SHA256 of "t.{t}.{body}">, with one v1 per active signing secret.

Example:

"t=1791763200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"

Flow-Event-Id
string
required

The event's id, also in the body. Use it to deduplicate. An event ID, evt_ and a ULID.

Pattern: ^evt_[0-9A-HJKMNP-TV-Z]{26}$
Example:

"evt_01JB8ZE5N7Q9S1V3X5Z7B9D1E3"

Flow-Event-Type
enum<string>
required

The event's type, also in the body.

  • message.received: the contact sent something with content (a button tap arrives as button_reply content).
  • message.sent, message.delivered, message.read, message.failed: the status of your outbound messages. message.failed carries the error in data.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 a message.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.
Available options:
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
Flow-Version
string<date>
required

The API version the event body is shaped by (your app's pinned version).

Body

application/json

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).

id
string
required

An event ID, evt_ and a ULID.

Pattern: ^evt_[0-9A-HJKMNP-TV-Z]{26}$
Example:

"evt_01JB8ZE5N7Q9S1V3X5Z7B9D1E3"

created_at
string<date-time>
required

When the event was created. Events are ordered in the log by commit, not by this time.

app
string
required

An app ID, app_ and a ULID.

Pattern: ^app_[0-9A-HJKMNP-TV-Z]{26}$
Example:

"app_01JB8Z0A1C3E5G7J9K1M3P5R7T"

livemode
boolean
required

Whether the event belongs to live mode.

timing
object
required

When the event moved through Flow, to measure Flow's overhead (delivered_at minus received_at).

type
enum<string>
required

Always message.received.

Available options:
message.received
data
object
required

The message the event is about.

conversation
object

The conversation an event happened in, inlined so most handlers need no extra call.

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.

reply

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.

fallback

What to send when the channel cannot show a piece of reply, as on HTTP sends. It applies to every piece.

Available options:
auto