For the complete documentation index, see llms.txt. This page is also available as Markdown.

Send events to a webhook

A webhook pushes your app's events to an endpoint you own, as they happen — a visitor starting a chat conversation, each message in that conversation, a server going live. Instead of polling the API, your system is told.

Every app can have up to 10 webhooks, each with its own destination, its own list of events, and its own signing secret. Webhooks are available on every plan; open an app and click Webhooks in the sidebar.

Because a webhook sends your app's activity to an endpoint of your choosing, only workspace owners can add, edit, or remove webhooks. Members can view the configuration and the delivery log, but not change it.

Adding a webhook

  1. Open your app and click Webhooks in the sidebar.

  2. Click Add webhook and fill in:

    • Name — a label so you can tell webhooks apart (e.g. "CRM sync").

    • Destination URL — the https:// endpoint that receives the POSTs. It must be publicly reachable; private and internal addresses are rejected.

    • Events — which events to send (at least one).

    • Include message text — off by default. When off, message events carry metadata only. See What gets sent.

  3. Leave Enabled on, then click Create webhook.

  4. Copy the signing secret. It is shown once and cannot be read again. If you lose it, use Rotate secret to mint a new one.

Then click Test on the webhook to send a webhook.test ping and confirm your endpoint answers.

Events

Event
Fires when

conversation.created

A visitor starts a new chat-widget conversation.

message.created

A message is added to a conversation — one per user turn and per assistant reply.

server.published

An MCP server is published (or redeployed) to production.

webhook.test is also delivered when you click Test. It is not subscribable: it goes only to the webhook you tested.

Conversation events come from the chat widget. They do not depend on transcript retention — a widget that stores nothing still emits them.

What a delivery looks like

Each delivery is a POST with a JSON body:

and these headers:

Header
Value

X-FlowCP-Signature

t=<unix seconds>,v1=<hex HMAC-SHA256> — see below.

X-FlowCP-Event

The event type, e.g. message.created.

X-FlowCP-Event-Id

The event id. Stable across webhooks and redeliveries.

X-FlowCP-Delivery-Id

This attempt's delivery id.

X-FlowCP-Webhook-Id

Which webhook this was sent to.

Answer with any 2xx status. Anything else counts as a failure and is retried.

conversation.key is a one-way hash of the visitor's conversation handle — it correlates every event of one thread without exposing the handle itself.

What gets sent

Payloads carry ids, roles, counts, names and timestamps. They never carry end-user access tokens, the visitor's conversation handle, tool arguments, or upstream API responses.

Message text is withheld by default. A message.created event normally reports the role, its position in the thread, how long the text was, how many attachments it had, and which tools an assistant turn called — but not the words. Turn on Include message text for a webhook to add data.message.content (capped at 20,000 characters) to its deliveries. Only do this for endpoints you are comfortable sending customer conversations to.

Verifying the signature

Every request is signed with the webhook's secret, so you can prove it came from FlowCP and is not a replay. The signature is an HMAC-SHA256 over <timestamp>.<raw request body>, hex-encoded:

Verify against the raw body bytes, before any JSON parsing and re-serialization — a re-serialized object will not match.

Delivery, retries, and the delivery log

A delivery is attempted up to 5 times with exponential backoff, spreading retries over roughly ten minutes. Click Deliveries on a webhook to see recent attempts with their status, HTTP response code, attempt count, and error message, and to Redeliver any of them. A redelivery replays the stored event with the same event id — de-duplicate on X-FlowCP-Event-Id if that matters to you.

Delivery history is kept for 14 days.

If a webhook's deliveries keep failing, it shows a Delivery error badge with the latest message. After 20 consecutive failed deliveries it is disabled automatically and says so; fix the endpoint and re-enable it to resume.

Events are queued before they are sent, so a delivery survives a restart on our side. Ordering is not guaranteed — use createdAt and message.sequence if you need to order events yourself.

Rotating or removing a webhook

  • Rotate secret mints a new signing secret and shows it once. Deliveries signed with the old secret stop verifying immediately, so update your endpoint promptly.

  • Edit changes the destination, the events, or the content setting.

  • Delete removes the webhook and its delivery history; no further events are sent to its endpoint.

Last updated