Docs
Open in panel → /app · deliveries
Delivery webhooks
Kumo delivery events land at POST /v1/webhooks/kumo/delivery. The
endpoint is HMAC-verified and idempotent by event id. The panel
lists the last 50 rows via GET /v1/panel/deliveries.
Contract
POST /v1/webhooks/kumo/delivery
X-Postmta-Signature: t=1700000000,v1=<hex(hmac-sha256(secret, t + '.' + body))>
X-Postmta-Event-Id: <dedupe key>
Content-Type: application/json
{
"workspace_id": "00000000-0000-4000-8000-000000000001",
"event": "bounce" | "deferred" | "complaint" | "delivered",
"address": "user@example.com",
"reason": "mailbox full",
"ts": "2026-07-12T00:00:00Z"
}
The t (timestamp) must be within ±300 s of server time. The signature
covers t + '.' + raw-body (HMAC SHA-256 hex).
Wiring
infra/kumomta-local/policy/hooks_delivery.lua is loaded by init.lua
when present. apps/api/src/policyRender.ts writes
managed/webhook_config.lua with URL/secret placeholders from env:
WEBHOOK_DELIVERY_URL=http://127.0.0.1:4000/v1/webhooks/kumo/delivery
WEBHOOK_DELIVERY_SECRET=<shared secret>
Optional fan-out targets live in webhook_endpoints (per-workspace URL
- secret rows).
Live appliance path
renderWorkspacePolicy writes the active workspace, shared secret, and
http://127.0.0.1:4000/v1/webhooks/kumo/delivery into
managed/webhook_config.lua. The local Kumo installer copies
hooks_delivery.lua beside init.lua, where it is loaded when present.
After rendering, bump Kumo’s config epoch so managed policy readers refresh.
Run bash scripts/kumo-delivery-smoke.sh with Kumo on :8000 and local
Supabase running; the script starts a temporary API on :4000 when needed.
The probe signs a delivery with the API’s signDelivery helper, posts it to
the live endpoint, verifies the webhook_deliveries row in Postgres, and
prints its row id. Battle-test step 22 runs this probe only when Kumo liveness
and the Supabase REST API are available.
Idempotency
X-Postmta-Event-Id is the unique key on webhook_deliveries
(unique (workspace_id, event_id)). Retries return
{ ok: true, idempotent: true } without failing the caller.
Side-effects
| event | action |
|---|---|
bounce / complaint |
insert into suppressions with reason=event, source='webhook' |
deferred / delivered |
delivery row only |
| any | append row in webhook_deliveries |
Tests
apps/api/src/webhooks/delivery.test.ts— HMAC sign/verify, signed POST success, bad HMAC 401, bounce → suppression, idempotent 23505.
Related
- infra/kumomta-local/policy/hooks_delivery.lua
apps/api/src/webhooks/delivery.ts— handler + panel list endpointsupabase/migrations/20260713133936_webhook_deliveries.sql— schema
Last verified: Sun Jul 12 2026 17:00:00 GMT-0700 (Pacific Daylight Time). Cross-link maintained byscripts/check-docs-drift.sh (M54).