Reference sheet · lesson 3
tawk.to webhooks
Four events, one signature, one retry policy. Everything from the developer page, compressed.tawk.to webhooks reference
Events and fields
| event | When | Payload |
|---|---|---|
chat:start | First message by visitor or agent (not whispers or trigger notifications) | chatId, time, domain, referrer, message{ text, type: msg|file|webrtc-call, sender{ type: agent|visitor|system } }, visitor{ name, email?, city, country }, property{ id, name } |
chat:end | Chat ended | chatId, time, domain, referrer, visitor, property |
chat:transcript_created | After all chats in a session ended + 3 minutes of inactivity | time, domain, referrer, chat{ id, visitor, messages[{ sender{ t: a|v|s, n?, id? }, type, msg?, time, attchs?[{ type, content.file{ url, name, mimeType, size, extension } }] }] }, property |
ticket:create | New ticket | time, property, requester{ name, email, type? }, ticket{ id, humanId, subject, message } |
sender.t: a agent, v visitor, s system. No per-message event exists. Custom attributes and tags are not in any payload.
Signature
- Header
X-Tawk-Signature= HMAC-SHA1(secret, raw body), hex. - Verify the raw bytes (
await request.text()) beforeJSON.parse; compare withcrypto.timingSafeEqualafter a length check.
import { createHmac, timingSafeEqual } from "node:crypto";
const expected = Buffer.from(createHmac("sha1", secret).update(rawBody).digest("hex"), "hex");
const given = Buffer.from(header ?? "", "hex");
const ok = given.length === expected.length && given.length > 0 && timingSafeEqual(given, expected);
Retries
- Non-2XX, or no response within 30 s → retried for up to 12 hours.
X-Hook-Event-Idis constant across retries → idempotency key in a shared store.- Return 2XX for events you ignore. Respond first; slow work after (
waitUntilfrom@vercel/functions, or a queue).
Dashboard
Administration → Settings → Webhooks → Create Webhook: name, HTTPS endpoint URL, events. Up to 10 webhooks per property. Enable/disable, edit, delete from the list.tawk.to Help: creating and managing webhooks
Vercel handler (examples/api/tawk-webhook.ts)
export async function POST(request: Request) {
const rawBody = await request.text();
const result = await handleTawkWebhook(
{ rawBody, signature: request.headers.get("x-tawk-signature"), eventId: request.headers.get("x-hook-event-id") },
{ secret: process.env.TAWK_WEBHOOK_SECRET ?? "", store },
);
return new Response(result.body, { status: result.status });
}
Order inside handleTawkWebhook: secret present → verify (401) → parse (400) → dedupe (200 duplicate) → store → side effects → 200 ok <event>.
Test from your machine
cd examples
npx tsx scripts/send-test-webhook.ts https://your-app.vercel.app/api/tawk-webhook "$TAWK_WEBHOOK_SECRET" chat:transcript_created --id t1
npx tsx scripts/send-test-webhook.ts https://your-app.vercel.app/api/tawk-webhook "$TAWK_WEBHOOK_SECRET" chat:start --tamper