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.*), plusbridge.ask.repliedandjob.completed. It does not carry baton events.
Baton events are per-baton, not account-level: registering
baton.writtenon 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:
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:
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 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 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.