This is more of a technical article but in short, Commslayer webhooks deliver real-time event notifications to your server over HTTPS.
Example use cases
-
Sync tickets to your CRM: Listen for
conversation_createdandconversation_updatedevents to automatically create and update records in Salesforce, HubSpot, or any CRM, keeping your sales team in the loop without manual data entry. -
Trigger order actions from customer replies: Use
message_createdevents to detect when a customer confirms a cancellation or address change, then call your fulfillment API to process it instantly. - Feed support data into your analytics pipeline: Stream every event to a data warehouse like BigQuery or Snowflake to build custom dashboards, track resolution times, or run sentiment analysis across all customer interactions.
Every delivery is signed with HMAC-SHA256 using a shared secret, includes a timestamp for replay protection, and carries a stable delivery_id for deduplication. Failed deliveries are retried up to 20 times with exponential backoff, and a circuit breaker protects both sides during extended outages. You can test any event type on demand, inspect delivery logs, and redeliver missed events through the API.
1. Signature header name
X-Commslayer-Signature, value format sha256=<signature>. Every delivery also carries X-Commslayer-Timestamp, X-Commslayer-Delivery-ID, and X-Commslayer-Event. Read header names case-insensitively.
2. HMAC/hash algorithm
HMAC-SHA256. The key is your webhook's secret - a 64-character hex string returned once in the POST /api/integration/v1/webhooks create response, and again only from POST .../webhooks/:id/rotate_secret.
3. Exact bytes that are signed
"{timestamp}.{raw_request_body}" - the Unix-seconds timestamp string, a literal dot, then the request body exactly as received. Always verify against the raw bytes; never re-serialize the parsed JSON.
4. Signature encoding
Lowercase hex, prefixed with sha256=.
5. Timestamp header and replay window
X-Commslayer-Timestamp, Unix epoch seconds. We don't enforce a window server-side - reject anything more than 5 minutes from your clock. Every retry and redelivery is re-signed with a fresh timestamp, so the tight window never conflicts with our retry schedule. Replay protection = valid signature + fresh timestamp + dedupe on delivery_id from the payload (see 7); the payload copy is covered by the signature, so it can't be forged or swapped.
6. Retry schedule and timeout
10-second timeout per attempt; success is any 2xx - return it before doing heavy processing. On failure: up to 20 attempts with exponential backoff (5s, 10s, 20s, doubling, capped at 1 hour), spanning roughly 10.5 hours. Sustained failures trip a circuit breaker that pauses deliveries for up to 1 hour; anything dropped during a pause appears in GET .../webhooks/:id/delivery_logs with status skipped and can be resent via POST .../webhooks/:id/delivery_logs/:log_id/redeliver. Roughly a day of continuous failure disables the webhook and emails your account admins; recover with POST .../reset_circuit_breaker or by re-enabling the webhook, then redeliver what you missed.
7. Unique delivery ID
In two places with the same value: a top-level delivery_id field inside the payload (covered by the signature - use this as your dedupe key) and the X-Commslayer-Delivery-ID header (convenience copy). It's stable across all retries and manual redeliveries of the same delivery. The event type is in the payload's event field and the X-Commslayer-Event header. Duplicates and out-of-order arrival are both possible - dedupe on delivery_id and don't assume ordering.
8. Example payload and verification code
POST /api/integration/v1/webhooks/:id/test sends a realistic sample of any event type through the production delivery path - identical headers, signing, and payload structure to real events, delivered once with no retries. A message_created payload looks like:
{
"event": "message_created",
"id": 999001,
"content": "Hello, I need help with my recent order #12345...",
"content_type": "text",
"message_type": "incoming",
"private": false,
"created_at": "2026-08-10T13:47:57Z",
"source_id": null,
"conversation": { "id": 1001, "status": "open", "...": "..." },
"inbox": { "id": 1, "name": "Support Inbox" },
"sender": { "...": "..." },
"account": { "id": 1, "name": "..." },
"attachments": [],
"delivery_id": "7424357d-9166-4f47-98c7-9b39af82f0a1"
}
Verification (Node):
const crypto = require('crypto');
function verifyWebhook(rawBody, headers, secret) {
const ts = headers['x-commslayer-timestamp'];
const sig = headers['x-commslayer-signature']; // "sha256=<hex>"
const expected = 'sha256=' +
crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
if (expected.length !== sig.length) return false;
const authentic = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
return authentic && fresh;
}
// After verification, dedupe on JSON.parse(rawBody).delivery_id
Two integration notes: parse payloads permissively (we may add fields over time - don't reject unknown properties), and monitor your webhook's health via GET /api/integration/v1/webhooks/:id, which exposes healthy, active, and circuit-breaker state.