Skip to content

Webhooks

Get message and call events on your backend as signed POSTs. Optional after you’ve set up session token issuance — use this for CRM, moderation, or offline call ring, not for basic chat.

New here?

Finish Quick start and Issue session tokens first.

What you need

  • Public HTTPS URL on your server
  • Webhook secret whsec_… (dashboard / account settings) — not an API key

Events

eventWhen
message.created / updated / deletedMessage lifecycle
call.started / ended / missedCall lifecycle (call.started → offline ring push)

Body: { event, roomId, message?, call? }.

Verify every request

Header: X-Webhook-Signature: sha256=<hex>
HMAC-SHA256 of the raw body with whsec_….

js
import { createHmac } from "node:crypto";

const expected =
  "sha256=" +
  createHmac("sha256", process.env.SENDSAR_WEBHOOK_SECRET)
    .update(rawBody)
    .digest("hex");

if (expected !== request.headers.get("x-webhook-signature")) {
  throw new Error("Invalid webhook signature");
}
php
$expected = 'sha256=' . hash_hmac(
    'sha256',
    $request->getContent(),
    config('sendsar.webhook_secret')
);

if (!hash_equals($expected, $request->header('X-Webhook-Signature', ''))) {
    abort(401, 'Invalid webhook signature');
}
go
mac := hmac.New(sha256.New, []byte(webhookSecret))
mac.Write(rawBody)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(expected), []byte(r.Header.Get("X-Webhook-Signature"))) {
    http.Error(w, "Invalid signature", http.StatusUnauthorized)
}

Return 2xx quickly; do heavy work async.

Next