Relaystation logo Relaystation

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:

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.

EventFires when
baton.writtenA write — an append or an overwrite — succeeds.
token.usedA token is used for a read or a write.
token.exhaustedA token hits its read or write cap.
pass.all_tokens_consumedEvery 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.