Webhooks
RTCstack delivers signed webhook payloads to your configured endpoints whenever LiveKit room events occur.
Configuring a Webhook
http
POST /v1/webhooks
Content-Type: application/json
{
"url": "https://your-app.com/webhooks/rtcstack",
"secret": "your-webhook-secret",
"events": ["room_started", "room_finished", "participant_joined", "participant_left", "egress_ended"]
}Response:
json
{
"id": "wh_abc123",
"url": "https://your-app.com/webhooks/rtcstack",
"events": ["room_started", "room_finished", ...],
"createdAt": "2024-04-21T10:00:00.000Z"
}Managing Webhooks
http
GET /v1/webhooks # list all
GET /v1/webhooks/:id # get one
PUT /v1/webhooks/:id # update url/secret/events
DELETE /v1/webhooks/:id # delete
POST /v1/webhooks/:id/test # send a test pingPayload Format
Every delivery is a POST with Content-Type: application/json and a signature header:
http
POST https://your-app.com/webhooks/rtcstack
Content-Type: application/json
X-RTCstack-Webhook-Signature: sha256=<hmac>
X-RTCstack-Webhook-Event: room_finished
{
"event": "room_finished",
"room": {
"id": "room-abc",
"name": "room-abc",
"createdAt": "2024-04-21T09:00:00.000Z",
"endedAt": "2024-04-21T10:00:00.000Z",
"numParticipants": 4
},
"timestamp": "2024-04-21T10:00:00.000Z"
}Verifying Signatures
ts
import { createHmac } from 'crypto'
function verifyWebhook(body: string, signature: string, secret: string): boolean {
const expected = 'sha256=' + createHmac('sha256', secret).update(body).digest('hex')
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}Always verify signatures before processing. Use timingSafeEqual to prevent timing attacks.
Delivery Retries
Failed deliveries (non-2xx response or timeout) are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 5 seconds |
| 2nd retry | 30 seconds |
| 3rd retry | 5 minutes |
After 3 retries, the delivery is marked failed. Use POST /v1/webhooks/:id/test to re-test connectivity.
Available Events
| Event | Trigger |
|---|---|
room_started | First participant joins a new room |
room_finished | Last participant leaves; room destroyed |
participant_joined | A participant successfully connected |
participant_left | A participant disconnected |
track_published | A participant published a track |
track_unpublished | A participant unpublished a track |
egress_started | Recording started |
egress_ended | Recording finished (includes file URL) |
ingress_started | RTMP/WHIP stream started |
ingress_ended | RTMP/WHIP stream ended |

