POST /api/events

The product. Fire one event; callhook handles all phone communication for it.

FieldTypeNotes
idstring · requiredUnique event id — also the idempotency key (unless idempotency_key is set). Duplicate ids never call twice.
typestring · requiredEvent type — see Events and Campaigns.
customer_idstring · requiredResolves the customer record from the business store; binds the whole session to one customer.
phonestringE.164 override. Defaults to the customer's stored number.
tzstringIANA timezone override (e.g. Asia/Kolkata) for the calling-hours check. Defaults by region.
payloadobjectEvent-type-specific data (invoice refs, warning reasons, offer details).
callback_urlstringWhere the terminal outcome is POSTed. Strongly recommended — this closes the loop.
not_beforestring · RFC3339Park the call until this time. Window rules still apply after it.

Responses: 202 {"status":"call_placed"} · 202 {"status":"scheduled"} (not_before) · 202 {"status":"deferred"} (outside calling hours) · 200 {"status":"duplicate"}.

POST /api/events/batch

Fire many events at once. Each is processed independently — one bad event never sinks the batch.

{ "events": [ { "id": "evt_1", "type": "invoice.due", "customer_id": "cus_1002" }, ... ] }

# → {"results":[{"session_id":"evt_1","status":"call_placed",...}, ...]}

POST /callhook/webhook

CALL-E's terminal results land here (set CALLHOOK_PUBLIC_URL so CALL-E can reach you). Requires the X-Callhook-Secret header when CALLHOOK_WEBHOOK_SECRET is configured — forged terminal results are rejected. You normally never call this yourself.

POST /mcp

The MCP server — callhook as agent tools (Streamable HTTP, JSON-RPC 2.0). Tools: fire_event, launch_campaign, get_campaign, list_sessions, list_event_types, run_demo. Point any MCP client here.

GET /api/stream

Server-Sent Events: session and campaign events pushed on every mutation. The war room's live updates ride this stream; polling is the fallback. With auth on, pass the token as ?token= (EventSource cannot send headers).

GET /api/sessions

All sessions with their state, audit trail, outcome and transcript. This is what the dashboard renders.

GET /api/metrics

{
  "sessions": 42,
  "by_status":  { "completed": 35, "failed": 2, "intake": 5 },
  "by_outcome": { "payment_promised": 18, "no_answer": 7, ... },
  "retries_armed": 3
}

GET /api/health

Uptime probe — also the quickest way to check your configuration at a glance.

{
  "ok": true,
  "dry_run": true,           // no CALLHOOK_API_KEY
  "windows_enforced": true,
  "auth_intake": false,      // CALLHOOK_INTAKE_TOKEN unset
  "auth_webhook": false
}

Authentication

When CALLHOOK_INTAKE_TOKEN is set, both intake endpoints require it:

Authorization: Bearer <CALLHOOK_INTAKE_TOKEN>

Point callhook at your real business data by implementing the business.Store interface (customers, invoices, promises, escalations) — see Architecture.