Webhooks
Configure signed webhook deliveries for contacts, conversations, and messages.
8 generated endpoints in this resource group.
/webhooksGet webhook settings#
Returns the current tenant webhook configuration, selected event types, and event types that can be subscribed to.
Authorization
Scheme
- bearerAuth
Required scopes
- webhook:read
Allowed roles
- owner
- admin
Read webhook settings.
Responses
- Name
200- Type
- application/json
- Description
- Webhook settings
- Name
default- Type
- application/json
- Description
- Error
Related schemas
Request
curl -X GET "https://api.flownally.com/v1/webhooks" \
-H "Authorization: Bearer {token}"Response
{
"id": "string",
"enabled": true,
"verified": true,
"url": "https://example.com",
"eventTypes": [
"contact.created"
],
"availableEventTypes": [
"contact.created"
],
"createdAt": "2026-04-27T00:00:00.000Z",
"updatedAt": "2026-04-27T00:00:00.000Z"
}/webhooksUpdate webhook settings#
Partially updates the single webhook configuration for the current tenant. Select event types from availableEventTypes.
Authorization
Scheme
- bearerAuth
Required scopes
- webhook:update
Allowed roles
- owner
- admin
Update webhook settings.
Request body
Send a application/json body. The body is required for this operation.
Optional attributes
- Name
enabled- Type
- boolean
- Description
- Optional request attribute.
- Name
url- Type
- string
- Description
- Optional request attribute.
- Name
eventTypes- Type
- array<WebhookEventType>
- Description
- Optional request attribute.
Responses
- Name
200- Type
- application/json
- Description
- Updated webhook settings
- Name
default- Type
- application/json
- Description
- Error
Related schemas
Request
curl -X PATCH "https://api.flownally.com/v1/webhooks" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"url": "https://example.com",
"eventTypes": [
"contact.created"
]
}'Response
{
"id": "string",
"enabled": true,
"verified": true,
"url": "https://example.com",
"eventTypes": [
"contact.created"
],
"availableEventTypes": [
"contact.created"
],
"createdAt": "2026-04-27T00:00:00.000Z",
"updatedAt": "2026-04-27T00:00:00.000Z"
}/webhooksDelete webhook settings#
Disables and removes the current tenant webhook configuration. Attempt logs remain visible for the retention window.
Authorization
Scheme
- bearerAuth
Required scopes
- webhook:delete
Allowed roles
- owner
- admin
Delete webhook settings.
Responses
- Name
204- Description
- Webhook settings deleted
- Name
default- Type
- application/json
- Description
- Error
Related schemas
Request
curl -X DELETE "https://api.flownally.com/v1/webhooks" \
-H "Authorization: Bearer {token}"Response
204 Webhook settings deleted/webhooks:verifyVerify webhook delivery#
Sends a signed synthetic webhook test event to the supplied URL or the saved webhook URL.
Authorization
Scheme
- bearerAuth
Required scopes
- webhook:update
Allowed roles
- owner
- admin
Verify webhook delivery.
Request body
Send a application/json body. The body is optional for this operation.
Optional attributes
- Name
url- Type
- string
- Description
- Optional request attribute.
Responses
- Name
200- Type
- application/json
- Description
- Verification result
- Name
default- Type
- application/json
- Description
- Error
Related schemas
Request
curl -X POST "https://api.flownally.com/v1/webhooks:verify" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com"
}'Response
{
"delivered": true,
"statusCode": 0,
"error": "string"
}/webhooks/attemptsList webhook delivery attempts#
Returns newest-first webhook delivery attempts from the last 7 days. Use this for delivery debugging, not long-term audit.
Authorization
Scheme
- bearerAuth
Required scopes
- webhook:read
Allowed roles
- owner
- admin
List webhook delivery attempts.
Optional parameters
- Name
pageSize- Type
- query integer
- Description
- Values above 100 are reduced to 100; 0 or omitted uses the default (50).
- Name
pageToken- Type
- query string
- Description
- Optional parameter.
- Name
eventType- Type
- query WebhookEventName
- Description
- Optional parameter.
- Name
status- Type
- query WebhookAttemptStatus
- Description
- Optional parameter.
Responses
- Name
200- Type
- application/json
- Description
- Webhook delivery attempts
- Name
default- Type
- application/json
- Description
- Error
Related schemas
Request
curl -G "https://api.flownally.com/v1/webhooks/attempts" \
-H "Authorization: Bearer {token}" \
-d pageSize="{pageSize}" \
-d pageToken="{pageToken}" \
-d eventType="{eventType}" \
-d status="{status}"Response
{
"webhookAttempts": [
{
"id": "string",
"eventId": "string",
"eventType": {},
"status": "succeeded",
"deliveryId": "string",
"attemptNumber": 0,
"attemptedAt": "2026-04-27T00:00:00.000Z",
"responseStatus": 0,
"responseBody": "string",
"latencyMs": 0,
"errorText": "string",
"signedWithVersions": [
0
]
}
],
"nextPageToken": "string"
}/webhooks/signing-keysList webhook signing keys for the current tenant#
Returns the active and any expiring (mid-rotation) webhook signing keys. On the very first call for a tenant, a key is bootstrapped automatically and its plaintext is included in the response — exactly once. Subsequent calls return metadata only (no plaintext).
Authorization
Scheme
- bearerAuth
Required scopes
- webhook:read
Allowed roles
- owner
- admin
List webhook signing keys.
Responses
- Name
200- Type
- application/json
- Description
- List of signing keys (plaintext present only on bootstrap response)
- Name
default- Type
- application/json
- Description
- Error
Related schemas
Request
curl -X GET "https://api.flownally.com/v1/webhooks/signing-keys" \
-H "Authorization: Bearer {token}"Response
{
"keys": [
{
"id": "string",
"version": 0,
"status": "string",
"createdAt": "2026-04-27T00:00:00.000Z",
"expiresAt": "2026-04-27T00:00:00.000Z",
"last4": "string",
"plaintextKey": "string"
}
]
}/webhooks/signing-keys:rotateRotate the webhook signing key#
Generates a new signing key for webhook deliveries and demotes the previous active key to "expiring" with a 24h overlap. During the overlap, both keys are valid signers; receivers can update at their own pace. Returns the plaintext of the newly-generated key exactly once.
Authorization
Scheme
- bearerAuth
Required scopes
- webhook:update
Allowed roles
- owner
- admin
Rotate a webhook signing key.
Responses
- Name
200- Type
- application/json
- Description
- New key generated; plaintext returned exactly once
- Name
default- Type
- application/json
- Description
- Error
Related schemas
Request
curl -X POST "https://api.flownally.com/v1/webhooks/signing-keys:rotate" \
-H "Authorization: Bearer {token}"Response
{
"key": {
"id": "string",
"version": 0,
"status": "string",
"createdAt": "2026-04-27T00:00:00.000Z",
"expiresAt": "2026-04-27T00:00:00.000Z",
"last4": "string",
"plaintextKey": "string"
}
}{configured webhook URL}Webhook payloads#
Endpoint shape implemented by external webhook receivers. Return any 2xx response after accepting an event. - Delivery is at least once. Deduplicate on the `webhook-id` header, which equals the payload `id` and stays the same on every retry of an event. The body is identical on every retry. - Delivery order isn't guaranteed, even within one conversation. For `message.updated`, keep the snapshot with the highest `message.revision`. - Each attempt times out after 15 seconds. Any 2xx response acknowledges the event. Redirects aren't followed. - Timeouts, connection errors, 3xx, 408, 429 and 5xx responses are retried about 30 seconds, 5 minutes and 30 minutes after the first attempt, for 4 attempts in total. Retry times include up to 20% random jitter, and the last retry happens within 30 minutes of the first attempt. - After a 429, a `Retry-After` value in seconds makes the next attempt wait at least that long. If that is later than 30 minutes after the first attempt, the event isn't retried. - 410 and any other 4xx response stop retries for that event. - An event that isn't delivered after its last attempt isn't sent again. Flownally never disables the endpoint automatically. - `webhook-timestamp` and `webhook-signature` are generated again for each attempt. - `GET /webhooks/attempts` lists delivery attempts from the last 7 days. - Events are delivered only while the webhook is enabled and verified. Changing `url` clears `verified`; deliveries resume after `POST /webhooks:verify` succeeds for the saved URL. Events from the time in between aren't delivered later.
Authorization
Scheme
- standardWebhooks
External receiver endpoint shape for webhook events.
Required parameters
- Name
Webhook-Id- Type
- header string
- Description
- Event identifier. Equals the payload `id` and stays the same on every retry of the event. Deduplicate deliveries with this value.
- Name
Webhook-Timestamp- Type
- header integer
- Description
- Unix timestamp in seconds for this delivery attempt; it changes on each retry. Reject stale timestamps to prevent replay.
- Name
Webhook-Signature- Type
- header string
- Description
- Standard Webhooks signature header, generated for each attempt. Verify it against `webhook-id`, `webhook-timestamp`, and the exact raw request body.
Request body
Flownally sends a application/json body. The body is required for this operation.
Payload variants
- Name
contact.created- Type
- ContactCreatedWebhookEvent
- Description
- Name
contact.updated- Type
- ContactUpdatedWebhookEvent
- Description
- Name
contact.archived- Type
- ContactArchivedWebhookEvent
- Description
- Name
conversation.started- Type
- ConversationStartedWebhookEvent
- Description
- A session started in a conversation. Sent once per session, when its first customer message or human agent's message arrives: either the message creates the session, or it arrives in a session that so far has only outbound automated messages. A session created `cold` by an outbound automated message (API, journey, campaign or chatbot) doesn't send this event when it's created. A conversation can have many sessions over its lifetime.
- Name
conversation.updated- Type
- ConversationUpdatedWebhookEvent
- Description
- An open session's status or owner changed; `changedFields` lists what changed, and changes that aren't visible in the payload (such as team access) send nothing. For example, a chatbot handing a session over to agents sends this event with `changedFields` `["status", "owner"]` and `session.status` `pending`. Starting and closing a session send `conversation.started` and `conversation.closed` instead, so `session.status` is never `closed` here.
- Name
conversation.closed- Type
- ConversationClosedWebhookEvent
- Description
- A session closed. Sent once for every session that closes, including a `cold` session with only outbound automated messages that nobody replied to; `session.started` is `false` for those. A closed session is never reopened; the next message starts a new session in the same conversation.
- Name
message.created- Type
- MessageCreatedWebhookEvent
- Description
- Name
message.updated- Type
- MessageUpdatedWebhookEvent
- Description
- Name
webhook.test- Type
- WebhookTestEvent
- Description
- Sent only by `POST /webhooks:verify` to confirm the endpoint. Respond with any 2xx. Carries no business data.
Responses
- Name
2XX- Description
- Event accepted
Payload
{
"id": "string",
"type": "contact.created",
"timestamp": "string",
"data": {
"contact": {
"id": "con_d7pibvs6gljrc2pfdc1g",
"tenantId": "tn_d7phk42ge9t9t6324f20",
"name": "Anna Kowalska",
"createdAt": "2026-04-30T09:20:00Z",
"updatedAt": "2026-04-30T10:10:00Z",
"conversations": [
{
"conversationId": "cnv_d7pigoip4tfcdebp5mlg",
"channel": "whatsapp",
"metadata": {
"phone_number": "+48123456789",
"wa_id": "48123456789"
}
}
],
"tagIds": [
"tag_d7pj8kaicb30377174j0"
],
"customFields": {
"cf_d7pgooc1v8t53cs68k5g": "customer",
"cf_d7pgpgin6fdi7ocdmdgg": "Krakow"
},
"archivedAt": null
}
}
}Response
2XX Event accepted