Skip to main content

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/webhooks (a key with manage_webhooks — all webhook CRUD requires that capability). 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.

Verify endpoint ownership

An unverified endpoint receives no deliveries — registration alone is not enough, and the misses are silent. On registration, RSMG Engage immediately POSTs a verification challenge to your URL (with the header X-RSMG-Engage-Event: verification, a 30 s timeout):

{ "type": "verification", "challenge": "<64-hex>", "timestamp": "<iso-8601>" }

Your receiver must respond 200 with JSON echoing the challenge back:

{ "challenge": "<the same value>" }

Once that round-trip succeeds, the endpoint is verified and event deliveries begin. Changing the endpoint's URL resets verification and re-triggers the handshake; you can also re-run it manually via POST /api/v1/webhooks/{webhookId}/verify.

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.
  • kid identifies 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-Key is stable across retries of the same event, so dedupe on it.
  • Failed deliveries are retried up to 3 attempts, with exponential backoff from a 1 s base (×2^(n−1), plus up to +50% jitter) and a 10 s per-attempt timeout. 10 consecutive failures open a per-endpoint circuit breaker (60 s cooldown); 50 consecutive failures auto-disable the endpoint — re-enable it in the console. All of these defaults are tunable per tenant/endpoint.
  • Return a 2xx quickly 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.