Set up a webhook
Register a URL to receive events like message.inbound, then verify the HMAC signature on every delivery.
Last updated 18 June 2026
Register an endpoint
Register a webhook URL — under Settings/Webhooks in the portal, or with POST /portal/webhooks. v1 wires the message.inbound event. The signing secret is returned exactly once at registration — store it; you'll need it to verify deliveries.
The message.inbound payload
When Servus receives an inbound message, it POSTs a JSON body to each subscribed URL:
{ "event": "message.inbound", "tenantId", "conversationId", "contactId", "messageId", "channel": "WhatsApp", "fromAddress": "+15551230000", "body": "Hi there", "receivedAtUtc": "…" }- Note:
channelhere is the channel name string (e.g. "WhatsApp"), the one place the API emits a name rather than a numeric ordinal.
Verify the signature — always
Every delivery carries an X-Servus-Signature header: an HMAC-SHA256 over the exact request body bytes, keyed by your signing secret, rendered as lowercase hex. Verify it before trusting the payload, against the raw body (before JSON-parsing — re-serializing can change bytes and break the signature), using a constant-time comparison.
- With the SDK:
verifyWebhookSignature(rawBody, req.header(SERVUS_SIGNATURE_HEADER), secret). - Without it:
createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex'), thentimingSafeEqualagainst the header. - A bad/missing signature should be rejected with
401and the payload ignored.
Retries and lifecycle
Webhooks retry with backoff on failure, so your endpoint should be idempotent (de-dupe on messageId). You can disable/enable or delete a subscription at any time. See the runnable verify-webhook.mjs example shipped with the SDK.