Docs/Webhooks
Docs/Events

Webhooks

Outbound webhook event types, HTTP headers, payload schemas, and signature verification.

Specification

Socketo delivers outbound HTTP POST webhook requests to your backend endpoints when channel occupancy changes or client events occur, adhering to the Pusher Channels Webhook specification.

Supported Webhook Events

EventTrigger ConditionPayload Properties
channel_occupiedFirst subscriber subscribes to channelchannel
channel_vacatedLast remaining subscriber leaves channelchannel
member_addedUser joins a presence channelchannel, user_id
member_removedUser vacates a presence channelchannel, user_id
client_eventClient triggers a client-* eventchannel, event, data, socket_id, user_id

Request Headers

Every outbound webhook delivery includes the following headers:

HeaderDescription
Content-TypeSet to application/json.
X-Pusher-KeyPublic application key (app.id).
X-Pusher-SignatureHex-encoded HMAC-SHA256 hash of the raw HTTP request body signed with app_secret.

Payload Schema

json
{
  "time_ms": 1710000000000,
  "events": [
    {
      "name": "channel_occupied",
      "channel": "chat-room"
    }
  ]
}

Signature Verification

Compare the incoming X-Pusher-Signature header against an HMAC-SHA256 calculation of the raw, unmodified request body:

Node.js Example

ts
import crypto from 'node:crypto'

export function verifyWebhookSignature(
  rawBody: string,
  signatureHeader: string,
  appSecret: string,
): boolean {
  const expected = crypto
    .createHmac('sha256', appSecret)
    .update(rawBody)
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader),
    Buffer.from(expected),
  )
}

Official SDK Verification

Official Pusher server SDKs verify webhooks directly:

ts
import Pusher from 'pusher'

const webhook = pusher.webhook({
  headers: Object.fromEntries(request.headers),
  rawBody: await request.text(),
})

if (!webhook.isValid()) {
  return new Response('Invalid signature', { status: 401 })
}

for (const event of webhook.getEvents()) {
  console.log(`Event: ${event.name} on ${event.channel}`)
}