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.