Agent-readable docs index: /docs/llms.txt. Full docs in one file: /docs/llms-full.txt. Download /docs/docs.zip to grep all markdown files locally.

Webhooks

A webhook tells your server when memory changes, so you can react without polling: refresh a cache, delete a copy after a forget, log a decision.

Set one up

  1. Write a small endpoint that accepts a POST (see Receive below).
  2. Add it in the dashboard (a project's webhooks page), or with an admin key: POST /v1/webhooks. The endpoint must be a public https URL; localhost, private and link-local addresses are rejected.
  3. Copy the signing secret (whsec_...). It is shown once. Store it as KAIBO_WEBHOOK_SECRET.
  4. Press send test on the endpoint, then open its delivery log to see the status and your server's response.
To try it from your laptop, expose your local server with a tunnel, for example cloudflared tunnel --url http://localhost:3000, and use the tunnel's https address.

Events

TypeWhendata
memory.createdA memory was derivedmemoryId, memoryType, supersedes, cancels
memory.supersededA memory was replaced or canceledmemoryId, supersededBy, status
memory.forgottenA forget completedreceiptId, memoryIds
Deliveries carry ids only, never memory text. Fetch content with the API. That also means a forgotten memory leaves no text in your delivery log or ours.

Verify

Each request has 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:
import { verifyWebhook } from "kaibo/webhooks"; const event = await verifyWebhook(rawBody, signatureHeader, secret);

Receive

Verify against the raw body, before parsing JSON.
import { 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"); }
A delivery body looks like this:
{ "id": "evt_...", "type": "memory.created", "createdAt": "2026-11-14T16:00:00.000Z", "projectId": "js76...", "data": { "memoryId": "kd7...", "memoryType": "decision", "supersedes": [], "cancels": [] } }
If verification fails, return a 4xx and log the reason. The usual causes are parsing the body before verifying, using the wrong secret, or a server clock more than five minutes off.

Delivery

Any 2xx is success. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours, then marked dead. Redirects are not followed and count as failures. Timeout is 10 seconds. List attempts with GET /v1/webhooks/{id}/deliveries; send a test with POST /v1/webhooks/{id}/test. A project can have 10 webhooks.