Platform
Events
Everything observable on your account is persisted as an event and consumable three ways. Same events, different delivery — pick per consumer.
Event types
| Type | Fired when |
|---|---|
call.started | A call (inbound or outbound) connects |
call.ended | The call hangs up |
call.transcribed | The final transcript is ready |
call.completed | Post-call insights (summary, sentiment) are ready |
call.takeover | A human takes over a live call |
message.received | Inbound SMS/WhatsApp lands on your number |
message.sent | An outbound message is accepted |
webhook.ping | You fire a test ping at a subscription |
Cursor polling
curl "https://klaw-voice.netlify.app/api/v1/events?type=call.ended&since=$CURSOR&limit=50" \
-H "Authorization: Bearer $KLAW_KEY"
Returns {"events": [...], "cursor": "..."} — pass the cursor back to resume where you left off.
Long-poll (block until something happens)
curl -X POST https://klaw-voice.netlify.app/api/v1/events/wait \
-H "Authorization: Bearer $KLAW_KEY" -H "Content-Type: application/json" \
-d '{"types": ["message.received", "call.ended"], "timeout_ms": 55000}'
Resolves with {"event"} the moment one lands, or {"event": null, "timed_out": true}. Max wait 55s — loop it. This is what the MCP wait_for_event tool uses.
Server-sent events
TOKEN=$(curl -s -X POST https://klaw-voice.netlify.app/api/v1/events/stream-token \
-H "Authorization: Bearer $KLAW_KEY" | jq -r .token)
curl -N "https://klaw-voice.netlify.app/api/v1/events/stream?token=$TOKEN"
Streams run ~50 seconds then close (reconnect with a fresh token); up to 6 concurrent streams per account. Permanent klw_ keys are refused in stream URLs — that's what the 120-second klwst_ token is for.
For push delivery to your servers, use Webhooks.