Webhook events

The request format of Thunderclap's webhooks. To set them up and verify them, see the webhooks guide.

Request

PartValue
MethodPOST
Content-Typeapplication/json
Thunderclap-Signaturet=<unix seconds>, then one v1=<hex> per active secret of the app
BodyA payload: { app, events }
request
{
  "app": "app_8f3k2m9q",
  "events": [
    { "id": "4c1d…", "time": 1761040800000, "type": "channel_occupied", "channel": "room-42" },
    { "id": "9a7e…", "time": 1761040800120, "type": "member_added", "channel": "presence-room-42",
      "userId": "ada", "info": { "name": "Ada" } }
  ]
}

Common fields

Every event has these fields:

FieldTypeDescription
idstringUnique id. Retries may deliver an event twice: deduplicate on it
timenumberWhen it happened, in milliseconds since epoch. Events can arrive out of order
typestringOne of the types below
channelstringThe channel it happened on

Event types

typeKindExtra fieldsSent when
channel_occupiedChannel existencenoneA channel got its first subscriber
channel_vacatedChannel existencenoneA channel has had no subscribers for 5 seconds
member_addedPresenceuserId: string, info?: unknownA user joined a presence channel
member_removedPresenceuserId: stringA user's last connection left a presence channel
client_eventClient eventsevent: string, data: unknown, socketId: string, userId?: stringA client sent an event; userId is set on presence channels
cache_missCache missesnoneA subscriber found a cache channel empty; once per channel until the next event

An endpoint receives only the kinds it was set up with in the dashboard.

Signature

Thunderclap-Signature carries a timestamp and one signature per active secret, each the HMAC-SHA256 of <t>.<body> in hex:

Thunderclap-Signature
t=1761040800,v1=5f2b…,v1=a91c…

verifyWebhook in @thunderclapjs/server checks it for you: it accepts any of the signatures and refuses timestamps older than 5 minutes.