Webhooks
Register an endpoint, understand the delivery guarantees, and debug what was sent.
A webhook is what we send you. We never use the word for anything inbound.
Events are emitted transactionally with the state change they announce, so an event exists if and only if the change committed. Delivery is at least once, signed, retried with backoff, and inspectable in a delivery portal.
Three pages cover them:
Event structure
The envelope, the full event-kind catalog, and what rides in
data.object.
Verifying signatures
How to authenticate a delivery before you trust a byte of it.
Prerequisites
- An HTTPS endpoint that responds quickly, 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": ["*"]
}
- Endpoints must be HTTPS.
- Pass
event_kindsto subscribe to a subset. Omit it for everything. - Endpoints are per environment. Register in sandbox and live separately, and store the two secrets separately.
GET /api/webhookslists them.DELETE /api/webhooks/{webhook_id}removes one.- The
whk_id is ours and stable. It survives any change of delivery infrastructure on our side, so it is safe to store against your own records.
Sandbox webhooks are real: delivered and signed exactly like live ones, so a consumer you build against sandbox is production ready as is.
Delivery guarantees
| Guarantee | What it means for your code |
|---|---|
| At least once | Deduplicate by event id. A retry reuses the same id |
| Retried with backoff | A non-2xx or a timeout is retried. Slow handlers cause duplicates |
Ordered by sequence, not arrival | Apply events in sequence order to make your projection deterministic |
| Emitted transactionally | You never receive an event for something that rolled back, and never miss one for something that did not |
| Signed | Verify 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. Every second your handler spends working is a second closer to a retry you did not need.
Deduplicate by event id
Retries and replays reuse the same id. Keep a processed-ids table and
skip anything you have seen.
Order by sequence
Retries and parallel delivery reorder arrivals. sequence is a
monotonically increasing per-partner counter, and it is the ordering
that matters.
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 and replay
GET /api/webhooks/portalreturns a link to the delivery portal: every attempt, response codes, response bodies, and manual retry. It is the first place to look when a webhook "never arrived", and it is where your endpoint's signing secret is shown.GET /api/webhook_eventsis the event history, everything we emitted whether delivered or not, paginated and carryingsequenceandpublished_at.POST /api/webhook_events/{event_id}/replayre-delivers one event. The envelope carries the sameidplus"replayed": true, so your dedupe still recognizes it as something you have already processed.
Recovering from downtime
Your endpoint being down does not lose events. It delays them.
- Deliveries retry with backoff automatically.
GET /api/webhook_eventslists everything, so page through anything newer than your last processedsequenceto backfill.- Replay individual events if you would rather re-run your handler than backfill by hand.
See Reconciliation for the full recovery procedure.