admin key: POST /v1/webhooks. The endpoint must be a public https URL; localhost, private and link-local addresses are rejected.whsec_...). It is shown once. Store it as KAIBO_WEBHOOK_SECRET.cloudflared tunnel --url http://localhost:3000, and use the tunnel's https address.| Type | When | data |
memory.created | A memory was derived | memoryId, memoryType, supersedes, cancels |
memory.superseded | A memory was replaced or canceled | memoryId, supersededBy, status |
memory.forgotten | A forget completed | receiptId, memoryIds |
X-Kaibo-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of "<t>.<raw body>" with your secret. Verify against the raw body, reject anything older than five minutes, and compare in constant time. The SDKs do all three:12import { verifyWebhook } from "kaibo/webhooks"; const event = await verifyWebhook(rawBody, signatureHeader, secret);
123456789import { verifyWebhook } from "kaibo/webhooks"; export async function POST(req: Request) { const raw = await req.text(); const event = await verifyWebhook(raw, req.headers.get("x-kaibo-signature"), process.env.KAIBO_WEBHOOK_SECRET!); if (event.type === "memory.forgotten") await deleteCopies(event.data.memoryIds as string[]); return new Response("ok"); }
1234567{ "id": "evt_...", "type": "memory.created", "createdAt": "2026-11-14T16:00:00.000Z", "projectId": "js76...", "data": { "memoryId": "kd7...", "memoryType": "decision", "supersedes": [], "cancels": [] } }
GET /v1/webhooks/{id}/deliveries; send a test with POST /v1/webhooks/{id}/test. A project can have 10 webhooks.