
# Outbound webhooks

A webhook lets Relaystation tell your system when something happens, instead of you polling for it. `GET /v1/webhooks/events` returns the live event-type allowlist.

There are **two delivery mechanisms**, and which one carries an event depends on the event:

- **Per-baton event triggers** carry the **baton** events (`baton.written`, `token.used`, `token.exhausted`, `pass.all_tokens_consumed`). You attach a trigger to a specific baton **at create time** — no account required, so the lodestone (no-account) path keeps working.
- **The account-level registry** (`POST /v1/webhooks`) carries the **product** events: e-sign (`envelope.*`) and ID-verification (`verification.*`), plus `bridge.ask.replied` and `job.completed`. It does **not** carry baton events.

> Baton events are per-baton, not account-level: registering `baton.written` on the account registry will not deliver. Attach a trigger to the baton instead (below).

## Baton events — per-baton triggers

Attach one or more triggers when you create the baton. Each trigger's signing **secret is returned once**, in the create response:

```bash
curl -X POST https://api.relaystation.ai/v1/baton \
  -H "Authorization: Bearer rs_live_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"preset":"ledger","tier":"femto","flags":{"hashChaining":true},
       "eventTriggers":[{"url":"https://your-app.example.com/hooks","events":["baton.written","token.used"]}]}'
```

The response carries `eventTriggers: [{ id, url, events, secret, ... }]` — capture `secret` (shown only here) to verify deliveries. Every baton event payload carries a `baton_id`, so one endpoint can serve many batons and filter by id.

| Event | Fires when |
|---|---|
| `baton.written` | A write — an append or an overwrite — succeeds. |
| `token.used` | A token is used for a read or a write. |
| `token.exhausted` | A token hits its read or write cap. |
| `pass.all_tokens_consumed` | Every recipient of a PASS baton has picked it up. |

Most Baton limit conditions you would want a webhook for (low egress, low time, low writes) are already surfaced as inline warnings on the API responses themselves, so you often do not need a webhook to see them coming.

## Product events — the account registry

Register a delivery URL and the product events you want. The signing **secret is returned once**:

```bash
curl -X POST https://api.relaystation.ai/v1/webhooks \
  -H "Authorization: Bearer rs_live_..." \
  -H "Content-Type: application/json" \
  -d '{"url":"https://your-app.example.com/hooks","events":["envelope.completed","verification.completed"]}'
```

**[E-sign](/docs/esigndoc)** fires seven, tracking the envelope lifecycle: `envelope.sent`, `envelope.viewed`, `envelope.signed` (per recipient), `envelope.completed` (everyone signed — go fetch the signed document), `envelope.rejected`, `envelope.expired`, `envelope.cancelled`.

**[ID verification](/docs/idverify)** fires seven, tracking the verification run: `verification.started`, `verification.document_processed`, `verification.age_check`, `verification.manual_review`, `verification.completed`, `verification.failed`, `verification.expired`.

**Async jobs** fire one, `job.completed` — delivered when an async job (the first is multi-page document analysis via `POST /v1/doc/analyze-async`) reaches a terminal state (`succeeded`, `failed`, or `expired`). The payload carries `{jobId, serviceKey, status, actualMicros?, result?, errorReason?}` — poll `GET /v1/jobs/{id}` for the same state, or react to this event.

(The bridge `ask` flow also delivers `bridge.ask.replied` through this registry.)

Account-registry management is dashboard/API-key only — an MCP connector token cannot register, update, or delete account webhooks.

## Delivery

Deliveries are signed: each carries an `X-Relaystation-Signature: sha256=<hex>` header — an HMAC-SHA256 over `${timestamp}.${rawBody}` (the timestamp is the `X-Relaystation-Timestamp` header, in seconds), computed with your signing secret. Recompute it with the same secret + concatenation to confirm the call really came from Relaystation. Deliveries also carry `X-Relaystation-Event` and `X-Relaystation-Delivery-Id`. Failed deliveries are retried with exponential backoff. A delivery endpoint that keeps failing is eventually disabled — check the Webhooks page in your dashboard for delivery history.

Webhook deliveries fire from a background worker, never inline in your API call — registering a webhook never slows your requests down.
