Webhooks

ByoTalk sends signed HTTP callbacks to your endpoints. Create endpoints in the dashboard (Environment → Webhooks) or with the API. The signing secret is shown once, when you create or rotate it.

Webhooks

Method & path Caller
POST/GET/PATCH/DELETE /v1/webhooks[/{id}] Server, dashboard
POST /v1/webhooks/{id}/rotate-secret Server, dashboard
POST /v1/webhooks/{id}/test Server, dashboard
GET /v1/webhooks/{id}/deliveries Server, dashboard
POST /v1/webhooks/{id}/deliveries/{deliveryId}/replay Server, dashboard
// POST /v1/webhooks
{ "url": "https://api.example.com/chat-webhooks",
  "events": ["message.created", "message.deleted", "member.added", "sink.failing"] }
// 201 (secret shown once)
{ "id": "wh_01J9...", "url": "...", "events": [...], "status": "enabled", "secret": "whsec_MfKQ9r8G..." }

Envelope and headers

POST /chat-webhooks HTTP/1.1
Content-Type: application/json
webhook-id: evt_01J9ZQ...
webhook-timestamp: 1791100000
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

Signature = base64(HMAC-SHA256(secret_bytes, ${webhook-id}.${webhook-timestamp}.${rawBody})), per Standard Webhooks. Reject timestamps more than 5 minutes off.

{
  "id": "evt_01J9ZQ...",
  "type": "message.created",
  "createdAt": "2026-10-04T10:06:00.120Z",
  "env": "env_01J9...",
  "data": { ... }
}

Catalogue

Event data Notes
message.created {conversationId, seq, message} Full message incl. text
message.updated {conversationId, seq, message} seq = event seq; message.seq = original
message.deleted {conversationId, seq, messageId, hard} No content
conversation.created {conversation}
conversation.updated {conversation, changes}
conversation.deleted {conversationId}
member.added {conversationId, seq, userIds, actorId}
member.removed {conversationId, seq, userId, actorId, reason: removed|left}
message.read {conversationId, userId, lastReadSeq} Coalesced per member per 10 s
user.deleted {userId, messages: anonymize|delete}
notification.needed {conversationId, messageId, recipientIds} Webhook-only push mode
sink.degraded {lagSeconds} Lag > 5 min
sink.failing {errorClass, since} No success > 1 h
sink.recovered {lagSeconds}
call.started {call} When the call starts ringing; see calls
call.ended {call} With endReason and durationSeconds

Delivery rules

  • Success = any 2xx within 10 s. Redirects are not followed.
  • Retries: 0, 10 s, 1 min, 10 min, 1 h, 3 h, 6 h, 12 h, 24 h, 24 h (+ jitter).
  • Not ordered. Use data.seq per conversation; dedup by webhook-id.
  • Endpoint disabled after 72 h with no success; owner emailed; replay available for 72 h of payloads.

Verifying signatures

Use the official Standard Webhooks library for your language (libraries) or our server SDK. Always verify against the raw request body, before JSON parsing.

// Node / Express — with byotalk/server
app.post("/chat-webhooks", express.raw({ type: "application/json" }), (req, res) => {
  try {
    const event = chatServer.webhooks.verify(req.body, req.headers); // throws on bad signature or > 5 min skew
    handle(event);                                                    // dedup on event.id
    res.sendStatus(200);
  } catch {
    res.sendStatus(401);
  }
});
// Node — without our SDK (what verify() does)
import crypto from "node:crypto";

function verify(rawBody: Buffer, headers: Record<string, string>, secret: string) {
  const id = headers["webhook-id"], ts = headers["webhook-timestamp"];
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) throw new Error("stale");
  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");    // base64-decode the part after whsec_
  const expected = crypto.createHmac("sha256", key).update(`${id}.${ts}.${rawBody}`).digest("base64");
  const ok = headers["webhook-signature"].split(" ").some((sig) => {
    const value = sig.split(",")[1] ?? "";
    return value.length === expected.length && crypto.timingSafeEqual(Buffer.from(value), Buffer.from(expected));
  });
  if (!ok) throw new Error("bad signature");
  return JSON.parse(rawBody.toString("utf8"));
}

Python, Go, PHP, Ruby and others: use the Standard Webhooks library for that language with the same whsec_... secret.