# Webhooks and events

An authorized owner or key registers a **public HTTPS** endpoint:

```json
{"org":"org_…","url":"https://your-runtime.example/ordan-events","events":["call.completed","message.received","port.activated"]}
```

`POST /v1/webhook-endpoints` returns a signing secret once. Store it
outside prompts and logs. Writes need org DEVELOPER or a key with
`webhooks:write`; reads use membership or `webhooks:read`. Agents can
poll their own events with `events.read`.

## Verifying deliveries

Deliveries are at-least-once. Verify against the **raw request body**,
enforce a timestamp replay window, deduplicate by the event `evt_…` id,
and return 2xx quickly — do longer work after durable receipt. Non-2xx
deliveries retry with backoff; owners can inspect and replay them.

`ordan-signature` is `t=<Unix seconds>,v1=<hex HMAC-SHA256>` over the exact
bytes `<Unix seconds>.<raw JSON body>`, using the endpoint secret as the
UTF-8 key. The same timestamp is in `ordan-timestamp`; event references
are in `ordan-event-id` and `ordan-event-type`. Accept a single
well-formed timestamp, reject events outside your replay tolerance (e.g.
five minutes), compare digests in constant time, and never re-serialize
JSON before verification.

Subscribe to exact event names or `"*"` for all; prefix globs like
`call.*` are rejected at registration.

## Event polling

Poll `GET /v1/events?since=evt_…&types=call.completed,message.received&limit=100`
when a webhook is missed. Advance the cursor only after processing its
rows. Duplicate and out-of-order callbacks never produce repeated
canonical status transitions.

Relevant events include call lifecycle/DTMF/recording/transfer, message
received/delivered/failed/finalized, Line activation/release, port
draft/pending/attention/cancel-requested/confirmed, and billing
settlement events (`call.billing.settled`, `call.billing.reconciled`,
`usage.billing.reconciled`). Operational end events and financial
settlement are distinct: finance can settle after the call ends.
