Webhooks
Mawjly POSTs HTTP webhooks to your endpoint when interesting things happen on a channel — useful for keeping your database in sync, triggering side effects, or building custom analytics.
Configure
Dashboard → your app → Webhooks → Add webhook. Pick the events you want, paste your endpoint URL, save. Mawjly returns the signing secret — copy it now, it won’t be shown again. Use it to verify each request.
Event types
| Event | Fires when |
|---|---|
channel_occupied | A previously empty channel gains its first subscriber |
channel_vacated | The last subscriber leaves a channel |
member_added | A new member joins a presence channel (first connection from that user_id) |
member_removed | The last connection for a presence member disconnects |
client_event | A subscriber publishes a client- event on a private/presence channel |
subscription_count_changed | Subscriber count on a channel changes (delivered with throttling) |
Payload shape
A single POST may batch multiple events:
{
"time_ms": 1748812345000,
"events": [
{ "name": "channel_occupied", "channel": "presence-room-42" },
{ "name": "member_added", "channel": "presence-room-42", "user_id": "alice" },
{
"name": "client_event",
"channel": "private-x",
"event": "client-typing",
"data": "{\"user\":\"alice\"}",
"socket_id": "123.456789"
}
]
}Headers
Content-Type: application/json
X-Pusher-Key: YOUR_APP_KEY
X-Pusher-Signature: <hex_hmac_sha256_of_body>The signature is computed over the raw request body using your app’s secret (not the per-webhook signing secret — that one is for older versions; current Pusher protocol signs with the app secret).
Verify in PHP
$body = file_get_contents('php://input');
$expected = hash_hmac('sha256', $body, $appSecret);
$received = $_SERVER['HTTP_X_PUSHER_SIGNATURE'] ?? '';
if (! hash_equals($expected, $received)) {
http_response_code(401);
exit;
}
$payload = json_decode($body, true);
foreach ($payload['events'] as $event) {
// process
}Verify in Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
app.post('/webhooks/mawjly', express.raw({ type: 'application/json' }), (req, res) => {
const body: Buffer = req.body;
const sig = req.header('X-Pusher-Signature') ?? '';
const expected = createHmac('sha256', process.env.MAWJLY_SECRET!).update(body).digest('hex');
if (!timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.sendStatus(401);
}
const { events } = JSON.parse(body.toString('utf8'));
for (const event of events) {
// process
}
res.sendStatus(200);
});Reliability
- Webhooks are delivered at-least-once. Make your handler idempotent.
- Mawjly retries non-2xx responses with exponential backoff.
- Each delivery’s status, response code, and duration are recorded — visible from Webhooks tab → … menu → View deliveries.
Replay a failed delivery
In the dashboard’s webhook delivery log, click any failed row to re-fire the same payload. Useful for testing your handler after a fix without waiting for new events.