Webhooks
Webhooks deliver engagement events to your backend over HTTPS — the server-to-server counterpart
to the SSE stream. You register an endpoint, RSMG Engage POSTs signed JSON to
it, and you verify the signature before trusting the payload.
Register an endpoint
Create endpoints in the console, or via POST /api/v1/admin/webhooks (admin / moderate_content).
Choose which event types to receive, or use * for all. The subscribable events are:
comment_created, comment_edited, comment_deleted, comment_approved, comment_rejected,
comment_pinned, comment_unpinned, reaction_toggled, discussion_locked,
discussion_unlocked, discussion_archived.
Delivery headers
POST <your-url>
Content-Type: application/json
X-RSMG-Engage-Signature: t=<unix>,v1=<hex>,kid=<8-hex>
X-RSMG-Engage-Timestamp: <iso-8601>
X-RSMG-Engage-Event: <event-name>
X-RSMG-Engage-Delivery-Id: <uuid>
X-RSMG-Engage-Idempotency-Key: <event>:<source-id>
Verify the signature
The scheme mirrors identity signing: HMAC-SHA256 with the endpoint's
secret. Here, though, the signed payload is ${timestamp}.${rawBody} — the raw request body
bytes as received, not a re-serialized copy.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(headers, rawBody, secret) {
const ts = headers['x-rsmg-engage-timestamp'];
const parts = Object.fromEntries(
headers['x-rsmg-engage-signature'].split(',').map((p) => p.split('=', 2)),
);
const expected = createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(parts.v1 ?? '', 'hex');
return a.length === b.length && timingSafeEqual(a, b);
}
- Capture the raw body before any JSON parsing or re-serialization — re-encoding changes the bytes and breaks verification.
- Reject deliveries whose timestamp is older than a few minutes, as replay protection.
kididentifies which secret to use — important during secret rotation, which keeps a 24-hour overlap, like identity signing.
Idempotency, retries & circuit breaking
X-RSMG-Engage-Idempotency-Keyis stable across retries of the same event, so dedupe on it.- Failed deliveries are retried with backoff. Persistent failures trip a per-endpoint circuit breaker, and a long run of failures auto-disables the endpoint — re-enable it in the console.
- Return a
2xxquickly and do slow work asynchronously, so you don't trip the breaker.
The full payload shape for each event is under Webhooks in the API Reference.