Webhooks
Flip provides webhooks for event-driven integrations.
Webhooks are used by integrations to subscribe to a range of event types and receive webhook messages when these events are happening on the Flip platform.
For example, a chat bot application would subscribe to chat.message.sent
events and receive a webhook message whenever a new chat message was sent to it.
Getting Started
In order to use webhooks, you have to be admin of an instance of Flip and have a server that can receive webhook messages via HTTPS. Setting up a new webhook requires these steps:
- create an API client in the web admin console, and ensure it has the
WEBHOOK_MANAGEMENT
permission - retrieve an access token (see API Authentication)
- register a new webhook (see Webhook API)
Webhook Subscriptions
You can subscribe to multiple different event types, but for each webhook you have to subscribe to at least one. The subscribed event types are specified during webhook registration.
Event types use hierarchical identifiers, e.g. chat.message.sent
.
An event type specification can match exactly one event type, or it can match a prefix of multiple event types.
For example, chat
would match any event type of the Chat domain, and chat.message
would match any event type related to Chat Messages.
A subscription can contain multiple event type specifications.
For example, a chat bot that handles onboarding can subscribe to channel.member.added
to track new employees, and to chat.message.sent
in order to exchange chat messages with them.
Receiving Messages
When you subscribe to events with a webhook, Flip will send you messages to the registered URL. To receive a webhook message, we suggest these steps:
- Check if the request comes with all expected message headers:
webhook-id
,webhook-timestamp
, andwebhook-signature
. - Ignore the message, if you already processed a message with the same
webhook-id
. - Check
webhook-timestamp
to make sure the message is no older (or younger) than five minutes. - Check
webhook-signature
to ensure the message is authentic. - Check the
tenant
field in the message body, if you write a multi-tenant integration. - Consume the events in the message body.
In return for this, you get two amazing delivery guarantees:
- Reliability: Flip will retry delivery of messages if a delivery attempt fails or times out. In consequence, please expect the same message to be sent more than once.
- Ordering: Flip will maintain the order of events during delivery. In consequence, Flip will not send more than one message at the same time for the same subscription. Unless the events of the previous message are delivered, the following events are retained in a buffer.
Message ID and Deduplication
You can use the webhook-id
header to check if you already processed a message.
In case of a downtime or network issue, a message could be delivered more than once to ensure that you actually received them (reliability guarantee).
Message Timestamps
Flip updates the message timestamp in the webhook-timestamp
header every time it attempts to deliver the message.
The timestamp should never deviate more than a few minutes from your system time.
If a timestamp is more than five minutes in the past or future, drop the message.
It could be forged or an attempt at a replay attack.
An authentic message will be re-delivered with an updated timestamp.
Message Signatures
The webhook-signature
header contains one or more base64-encoded HMAC-SHA256 signatures of the message content.
If there is more than one signature, each is separated with a space character.
A signature is prefixed with v1
to indicate that we currently use a symmetric encryption scheme.
Use the Webhook API to obtain the shared secret used to sign the message.
Shared secrets are prefixed whsec_
and base64-encoded.
The signed content consists of the dot-separated webhook-id
, webhook-timestamp
, and the JSON-serialized payload (e.g. 9b3999bf-291b-44a3-a409-de1e247eb301.1730192335.{"items":[{"id":"283
…).
Here is how we calculate the signature in Kotlin:
Here is an example of signed content:
84476261-219f-4f3c-9a3d-4184567c98dd.1745936362.{
"id" : "84476261-219f-4f3c-9a3d-4184567c98dd",
"items" : [ {
"type" : "my.event.type",
"data" : "SampleEvent123",
"timestamp" : "2025-04-29T14:19:22.403Z"
} ],
"recipient" : "633e26c2-fa98-4773-8f35-e70dce70b6c5",
"tenant" : "your-company"
}
With a key of whsec_VGhpcyBpcyBhIHNlY3JldCBrZXkgdXNlZCB0byBzaWduIHdlYmhvb2sgbWVzc2FnZXMh
, the signature for this message is v1,lKU3+t3uPFkG8HCe3Z26GMvbY2/ecF/TG7BaDbil3Xc=
.
Tenant and Recipient
The message body contains two additional fields:
tenant
contains the name of the Flip tenant for which the webhook was registered.recipient
contains the Flip user ID of the API client that registered the webhook.
You can use these fields to validate requests or debug issues in multi-tenant integrations.
Consuming Events
In general Webhook endpoints should react fast. Webhook messages can contain several hundred events and deliveries should not time out. We recommend receiving a message and putting the events in a local database or queue for subsequent processing.
Response Codes
Flip looks at the HTTP response code to decide if a message was delivered.
- Any
2xx
response code means the message was successfully received by the subscriber. - Any
5xx
response means temporary failure and Flip will retry to deliver the same message multiple times. - Any
4xx
response is treated as an unrecoverable error and will pause the webhook. - Any
3xx
response is treated as an unrecoverable error and will pause the webhook.
Paused webhooks have to be reactivated via API call before they will resume delivery.
Webhook States
You can inspect the status of a webhook by listing webhooks via the Webhook API.
Webhooks can have any of the following states:
- ACTIVE: The webhook will deliver messages with new events immediately.
- FAILING: An attempt to deliver a previous message failed.
Flip will retry to deliver the message at
next_retry
for a limited number of times over 3 days. Events are retained during that time, so no information is lost. The webhook is deactivated once retry attempts are exhausted. - PAUSED: The webhook is not delivering messages until it is resumed by an API call. Webhooks can be paused via API (to delay delivery, e.g. during a maintenance window). Flip pauses a webhook when delivering a message leads to an unrecoverable error (e.g. a 4xx response from the registered URL). Events are retained while the webhook is paused, so no information is lost. A paused webhook will be deactivated after 3 days.
- INACTIVE: The webhook is not delivering messages, and new events are not retained. Inactive webhooks will be deleted after 7 days, unless they are reactivated via API.
Security
Flip sends webhook messages to the URL provided on registration via HTTPS. Messages are cryptographically signed for authentication. Message content is not encrypted. We rely on the encrypted tunnel HTTPS provides. Flip will not accept webhook registrations for HTTP URLs.
Access and Visibility
Only API clients can currently register webhooks. In general, webhooks can only receive events that are also visible to the subscribing API client (limited visibility). For example, an API client that is not a member of a group chat will not receive an event when a user sends a message to that group. We recommend to create API clients that are regular users instead of admins.
Some events do not or not yet have a natural receiver and are instead published to any subscribing webhook from the same tenant (global visibility). This applies, for example, to auditing events. Subscription to globally visible events will potentially give an API client broad access to data and should be considered carefully.
List of Event Types
Channels
Event Type | Description | Visibility |
---|---|---|
channel.member.added | A member was removed from a channel that the API client is a member of. | Limited |
channel.member.removed | A member was added to a channel that the API client is a member of. | Limited |
Chat
Event Type | Description | Visibility |
---|---|---|
chat.message.sent | A chat message was sent to the API client. | Limited |
HR Absences
Event Type | Description | Visibility |
---|---|---|
hr.absence.requested | A user requested time off. | Global |
User Attribute Definitions
Event Type | Description | Visibility |
---|---|---|
user.attribute-definition.created | A user attribute definition was created. | Global |
user.attribute-definition.deleted | A user attribute definition was deleted. | Global |