Platform - Webhooks
Webhooks enable developers to extend the Liveblocks platform. From your system, you can listen to events that get automatically triggered as users interact with collaborative rooms.
Configuring webhooks
To set up webhooks for your project, you’ll need to create an endpoint, subscribe to events, and secure your endpoint.
Creating an endpoint
If you would like to create an endpoint to receive webhook events, you will do so from within the webhooks dashboard for your project.
From the dashboard overview, navigate to the project you’d like to add webhooks to.
Click on the webhooks tab from the left-hand menu.
Click the “Create endpoint…” button.
Enter the URL of the endpoint you would like to use. Configure with your own endpoint or generate a Svix playground link by clicking on "use Svix play".
Select the events you would like to subscribe to.
Click “Create endpoint”.
Your endpoint must return a 2xx
(status code 200-299
) to indicate that the
event was successfully received. If your endpoint returns anything else, the
event will be retried, see replaying events for more
details.
If all events fail to be delivered to your endpoint for 5 consecutive days, your endpoint will automatically be disabled. You can always re-enable it from the dashboard.
Edit endpoint events
You can easily edit the events you want to subscribe to after creating an endpoint.
Select the endpoint you would like to edit from the list of webhooks in the dashboard.
Select “Edit endpoint…” from the top right dropdown.
Update event selections and click “Save changes”.
Replaying events
If your service is unreachable, message retries are automatically re-attempted. If your service incurs considerable downtime (over 8 hours), you can replay individual messages from the Endpoints portion of the dashboard by clicking the kebab menu on an individual message, or you can opt to bulk replay events by clicking the top right dropdown and selecting “Recover failed messages…”.
Each message is attempted based on a schedule that follows the failure of the preceding attempt. If an endpoint is removed or disabled, delivery attempts will also be disabled. The schedule for retries is as follows:
- Immediately
- 5 seconds
- 5 minutes
- 30 minutes
- 2 hours
- 5 hours
- 10 hours
- 10 hours (in addition to the previous)
For example, an attempt that fails three times before eventually succeeding will be delivered roughly 35 minutes and 5 seconds following the first attempt.
Security verification
Verifying webhooks prevents security vulnerabilities by safeguarding against
man-in-the-middle, CSRF, and replay attacks. Because of this, it is essential to
prioritize verification in your integration. We recommend using the
@liveblocks/node
package to verify and return fully typed events.
Install the package
Set up the webhook handler
Set up your webhook handler, inserting your secret key from the webhooks dashboard you set up earlier into
WebhookHandler
.Verify an event request
We can verify a genuine webhook request with
WebhookHandler.verifyRequest
The method will return a
WebhookEvent
object that is fully typed. You can then use the event to perform actions based on the event type. If the request is not valid, an error will be thrown.Full example
Here’s an example from start to finish.
Manually verify in Node.js
It’s also possible to manually verify your webhooks in Node.js, though it’s unlikely this’ll be necessary.
How to manually verify webhook events in Node.js
Construct the signed content
The content to sign is composed by concatenating the request’s id, timestamp, and payload, separated by the full-stop character (
.
). In code, it will look something like:Generate the signature
Liveblocks uses an HMAC with SHA-256 to sign its webhooks.
So to calculate the expected signature, you should HMAC the
signedContent
from above using the base64 portion of your webhook secret key (this is the part after thewhsec_
prefix) as the key. For example, given the secretwhsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw
you will want to useMfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw
.For example, this is how you can calculate the signature in Node.js:
Validate the signature
The generated signature should match one of the signatures sent in the
webhook-signature
header.The
webhook-signature
header comprises a list of space-delimited signatures and their corresponding version identifiers. The signature list is most commonly of length one. Though there could be any number of signatures. For example:Make sure to remove the version prefix and delimiter (e.g.,
v1
) before verifying the signature.Verify the timestamp
As mentioned above, Liveblocks also sends the timestamp of the attempt in the
webhook-timestamp
header. You should compare this timestamp against your system timestamp and make sure it’s within your tolerance to prevent timestamp attacks.
Manually verify in Elixir
It’s also possible to manually verify your webhooks in Elixir using Plug/Phoenix, especially if you want to validate Liveblocks webhooks before parsing the request body.
How to manually verify webhook events in Elixir
Construct the signed content
The signed content is composed by concatenating the webhook ID, timestamp, and request body, separated by dots (
.
). In Elixir, it looks like this:webhook_id
comes from the"webhook-id"
header.webhook_timestamp
comes from the"webhook-timestamp"
header.body
is the raw request body.
Generate the signature
Liveblocks signs webhooks using HMAC with SHA-256. You need to use the base64-decoded portion of your secret (after the
whsec_
prefix) as the key.Example in Elixir:
Validate the signature
The signature you just generated should match one of the signatures from the
webhook-signature
header. That header contains space-separated values like:You should extract just the Base64-encoded signature (the part after the comma):
Then check if your generated signature is in the list:
Verify the timestamp
Liveblocks includes a
webhook-timestamp
header to help prevent replay attacks. You should check that the timestamp is within a reasonable window (e.g., 5 minutes):Full example
Here’s the full code, as detailed to far.
Add to your endpoint module
Finally, to use the validator to your endpoint module, place it before
Plug.parsers
.
Testing locally
Running webhooks locally can be difficult, but there are several tools that allow you to temporarily host your localhost server online.
Using svix-cli
The svix-cli
provides a listen
command that creates a publicly accessible URL for testing
webhooks without requiring any account setup or network configuration changes.
If your project is running on localhost:3000
, you can run the following
command to generate a temporary URL:
This will output a unique URL that forwards all POST requests to your local endpoint:
The generated URL can be placed directly into the Liveblocks webhooks dashboard
for testing. This approach is particularly useful in enterprise environments
where tools like localtunnel
or ngrok
may be blocked by security policies.
Using localtunnel or ngrok
Alternatively, you can use tools such as
localtunnel
or
ngrok
which also allow you to
temporarily host your localhost server online.
If your project is running on localhost:3000
, you can run the following
command to generate a temporary URL that’s available while your localhost server
is running:
If you visit the page localtunnel
links you to, and correctly input your IP
address, the URL it generates can be placed into the Liveblocks webhooks
dashboard for quick testing.
For a full step-by-step guide on testing with localtunnel
and ngrok
, read
the guide on
how to test webhooks on localhost.
Source IP Addresses
In case your webhook receiving endpoint is behind a firewall or NAT, you may need to allow traffic from Svix's IP addresses.
Liveblocks events
An event occurs when a change is made to Liveblocks data. Each endpoint you provide in the webhooks dashboard listens to all events by default but can be easily configured to only listen to a subset by updating the Message Filtering section.
The Event Catalog in the webhooks dashboard provides a list of events available for subscription, along with their schema.
Events available for use include:
StorageUpdated
UserEntered/UserLeft
RoomCreated/RoomDeleted
YDocUpdated
CommentCreated/CommentEdited/CommentDeleted
CommentReactionAdded/CommentReactionRemoved
ThreadCreated/ThreadDeleted/ThreadMetadataUpdated
Notification
More events will come later, such as:
MaxConnectionsReached
UserEnteredEvent
When a user connects to a room, an event is triggered, indicating that the user
has entered. The numActiveUsers
field shows the number of users in the room
after the user has joined. This event is not throttled.
UserLeftEvent
A user leaves a room when they disconnect from a room, which is when this event
is triggered. The numActiveUsers
field represents the number of users in the
room after the user has left. This event, like UserEntered
, is not throttled.
StorageUpdatedEvent
Storage is updated when a user writes to storage. This event is throttled at 60 seconds and, as such, may not be triggered for every write.
For example, if a user writes to storage at 1:00 pm sharp, the
StorageUpdatedEvent
event will be triggered shortly after. If the user writes
to Storage again at 1:00 pm and 2 seconds, the StorageUpdatedEvent
event will
be triggered 60 seconds after the first event was sent, around 1:01 pm.
On Enterprise plans we can increase the throttle rate.
RoomCreatedEvent
An event is triggered when a room is created. This event is not throttled. There are two ways for rooms to be created:
- By calling the create room API
- When a user connects to a room that does not exist
RoomDeletedEvent
An event is triggered when a room is deleted. This event is not throttled.
YDocUpdatedEvent
Yjs document is updated when a user makes a change to a Yjs doc connected to a room. This event is throttled at sixty seconds and, as such, may not be triggered for every write.
For example, if a user updates a Yjs document at 1:00 pm sharp, the
YDocUpdatedEvent
event will be triggered shortly after. If the user writes to
the Yjs document again at 1:00 pm and 2 seconds, the YDocUpdatedEvent
event
will be triggered 60 seconds after the first event was sent, around 1:01 pm
On Enterprise plans we can increase the throttle rate.
CommentCreatedEvent
An event is triggered when a comment is created. This event is not throttled.
CommentEditedEvent
An event is triggered when a comment is edited. This event is not throttled.
CommentDeletedEvent
An event is triggered when a comment is deleted. This event is not throttled.
CommentReactionAddedEvent
An event is triggered when a reaction is added to a comment. This event is not throttled.
CommentReactionRemovedEvent
An event is triggered when a reaction is removed from a comment. This event is not throttled.
ThreadCreatedEvent
An event is triggered when a thread is created. This event is not throttled.
ThreadDeletedEvent
An event is triggered when a thread is deleted. This event is not throttled. A thread is deleted when all comments in the thread are deleted or when the thread is manually deleted.
ThreadMetadataUpdatedEvent
An event is triggered when a thread metadata is updated. This event is not throttled.
ThreadMarkedAsResolvedEvent
An event is triggered when a thread is marked as resolved. This event is not throttled.
ThreadMarkedAsUnresolvedEvent
An event is triggered when a thread is marked as unresolved. This event is not throttled.
NotificationEvent
Notification events are designed to help you create notification emails for your users. By default, they’re triggered 30 minutes after an activity occurs, but this number can be modified in your dashboard inside a project’s settings.
This webhook event is triggered by both Liveblocks and custom notification
kinds
, as detailed below.
Thread notification
When using Comments, an event is triggered
30 minutes after a user has been mentioned or replied to in a thread, and has
not seen the thread. It will also be triggered if the user has subscribed to the
thread and has not seen the thread. The event won’t be triggered if the user has
seen the thread or unsubscribed from the room’s thread notifications. This is
the Liveblocks thread
notification kind.
If you want to easily identify this event in your code then you can use the type
guard
isThreadNotificationEvent
.
TextMention notification
When using Text editor, an event is
triggered 30 minutes after a user has been mentioned in a text and has not seen
the text mention. This is the Liveblocks textMention
notification kind.
If you want to easily identify this event in your code then you can use the type
guard
isTextMentionNotificationEvent
.
Custom notification
An event is triggered 30 minutes after the user has been notified of a custom
event and has not seen the notification. All custom notification kinds
are
prefixed with $
and are manually by you on the server. Learn more about
triggering custom notifications.
If you want to easily identify this event in your code then you can use the type
guard
isCustomNotificationEvent
.
Use Cases
With webhooks, you can subscribe to the events you are interested in, and be alerted of the change when it happens. Powerful ways to leverage webhooks with Liveblocks include:
- Storage synchronization between room(s) and an internal database
- Monitoring user activity in a room
- Notifying the client if maximum concurrency has been reached
Webhooks are an excellent way to reduce development time and the need for polling. By following the steps outlined in this guide, you’ll be able to configure, subscribe to, secure, and replay webhook events with Liveblocks.
If you have any questions or need help using webhooks, please let us know by email or by joining our Discord community! We’re here to help!