Platform ToolsWebhooks

Webhooks

Register an endpoint, understand the delivery guarantees, and debug what was sent.

A webhook is an event we send to your HTTPS endpoint when something changes: a deposit lands, an endorsement is decided, a wallet send confirms. This page covers registration, delivery, and recovery. The payload and its authentication have their own pages:

Prerequisites

  • An HTTPS endpoint that returns 2xx before it does any work.
  • Somewhere to store processed event ids, for deduplication.
  • A place to keep the endpoint's signing secret, per environment.

Registering an endpoint

curl -s $BASE/api/webhooks \
  -H "Api-Key: $KEY" -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://yourapp.example/rasto/webhook"}'
{
  "id": "whk_033y9PqRsTuVwXyZaBcDe",
  "object": "webhook",
  "url": "https://yourapp.example/rasto/webhook",
  "event_kinds": null,
  "secret": "whsec_..."
}
  • secret is shown once, in this response. It is the endpoint's signing secret: store it now, per environment. GET /api/webhooks never returns it. To get a new secret, delete the endpoint and register it again.
  • Endpoints must be HTTPS, with a url of at most 512 characters.
  • Omitting event_kinds subscribes to every kind, which reads back as null. To receive only some, see Subscribing to a subset.
  • Endpoints are per environment. Register in sandbox and live separately, and store the two secrets separately.
  • GET /api/webhooks lists them. DELETE /api/webhooks/{webhook_id} removes one.
  • The whk_ id is stable, so you can store it against your own records.

Sandbox webhooks are real: delivered and signed exactly like live ones, so the consumer you build against sandbox runs unchanged in live.

Delivery guarantees

GuaranteeWhat it means for your code
At least onceDeduplicate by event id. A retry reuses the same id
Retried with backoffA non-2xx or a timeout is retried. Slow handlers cause duplicates
Ordered by sequence, not arrivalApply events in sequence order to make your projection deterministic
Emitted transactionallyYou never receive an event for something that rolled back, and never miss one for something that did not
SignedVerify before parsing. See Verifying signatures

Processing

Verify the signature

Against the raw request body, before parsing it. Reject anything that does not verify, and anything whose timestamp is outside your tolerance window.

Acknowledge fast

Return 2xx as soon as the payload is stored, and process asynchronously. A handler that works before it answers risks a timeout, and a timeout is retried.

Deduplicate by event id

Retries and replays reuse the envelope's id. Keep a processed-ids table and skip anything you have seen.

Order by sequence

Retries and parallel delivery reorder arrivals, so order by sequence. It only ever increases, but it is not contiguous: gaps are normal, so never wait for a missing number.

Treat events as signals, state as truth

An event tells you something changed. For decisions that matter, re-read the resource with a GET rather than acting on a payload that may have been sitting in a retry queue.

Debugging, replay, and downtime

  • GET /api/webhook_events is the event history: everything we emitted, whether delivered or not, newest first. It is the first place to look when a webhook "never arrived": a published_at of null means it has not gone out yet. See Reading the history for the object.
  • POST /api/webhook_events/{webhook_event_id}/replay re-delivers one event, as many times as you call it. The envelope carries the same id plus "replayed": true, so your dedupe skips it if you already processed the original.
  • The dashboard shows the same history and replay under Webhooks.

An endpoint that is down delays events; it does not lose them. Deliveries retry with backoff, and every event stays in the history. To catch up after an outage, page through GET /api/webhook_events until you reach your last processed sequence, or replay the events you missed. For the full procedure, see Reconciliation.