Skip to content

Concepts

A webhook goes through Ricochet in two halves. Ingest receives the request on a source, checks it and stores it as an event, then answers the sender. Delivery sends the event to each destination its source is connected to, retrying until the destination accepts it. Because the event is stored before the sender gets an answer, nothing that was accepted is lost, and any event can be replayed later.

An organization holds your projects and members. A project is one product or service. Each project has up to three environments, dev, staging and production. A project starts with dev; you add the others in Settings. Everything below (sources, destinations, connections, events, API keys) belongs to one environment, so a test in dev never reaches production.

Create keys in Developers, choosing their identity and permission ceiling. Personal keys require their human owner’s current membership and permissions, capped by the original grant. Removing the member permanently revokes their personal keys; adding them back does not revive old credentials.

For production integrations, a human admin creates a service key with an independent viewer or developer grant in one environment. It continues working when its creator leaves, but cannot administer secrets, credentials, people or keys, inherit platform privileges, or act as its creator’s CLI identity. Only human admins revoke service keys. HTTP destination creation does not reveal its signing secret to service keys; have a human admin rotate it when needed.

Secrets are shown once. Revocation blocks subsequent authorization checks, not work already authorized or delivered. Key types cannot be changed in place. Platform administration requires a signed-in human, never an environment key.

A source is a URL providers send to, https://in.getricochet.dev/src_…. Each source has:

  • Methods it accepts: POST by default; GET, PUT, PATCH and DELETE can be added. Any content type is accepted.
  • Verification: how Ricochet checks the sender. The built-in methods are stripe, github, hmac (any HMAC signature header), basic, api_key and shared_secret; none accepts every request and stores it as not verified. An optional list of allowed IP ranges applies with any method. See the provider guides.
  • On failure: reject (the default) answers 401 and stores nothing. quarantine stores the request flagged and answers as usual, without delivering it; you can inspect it and release it from the dashboard.
  • Secrets, which are write-only. Adding a new one rotates: the previous secret keeps verifying for an overlap (one day by default, up to 30 days).
  • Deduplication (optional): with the stripe or github preset, a retry of a request Ricochet already stored, matched by the provider’s event or delivery ID, is answered without storing it twice, for 3 days by default.
  • Handshake (optional): answers a provider’s endpoint check, for Slack, Meta, Microsoft Graph and Dropbox, without storing it.
  • Response: what the sender gets for a stored request, 200 with an empty body unless you set a status from 200 to 204 and a short body.
Status When
The source’s response (200) Stored. The ricochet-event-id header names the event.
401 Verification failed and the source rejects failures.
404 No such source.
405 The source does not accept this method; Allow lists the ones it does.
413 The body or the headers are over the plan’s limits.
429 Over the source’s rate limit; Retry-After says when to try again.
503 The request could not be stored; Retry-After says when to try again.

Every answer carries a ricochet-request-id header, which names the request in the dashboard and in support requests.

A destination is where events go.

  • HTTP destinations take an http(s) URL on the public internet. Private, loopback and link-local addresses are refused, both when you save the URL and when each attempt resolves it, and redirects are not followed.
  • CLI destinations belong to one developer and deliver to ricochet listen on their machine. The CLI creates yours the first time you run it; teammates each get their own, so nobody receives somebody else’s events.

Each delivery is sent with the event’s original method, body and headers (minus hop-by-hop and proxy headers), plus:

Header Value
webhook-id The delivery’s ID: the same on every retry, new for a replay. Use it as an idempotency key.
webhook-timestamp When this attempt started, in Unix seconds.
webhook-signature v1,<signature> for each of the destination’s signing secrets. See Verifying signatures.
idempotency-key The delivery’s ID, as in webhook-id.
ricochet-event-id, ricochet-delivery-id, ricochet-source-id What this request is.
ricochet-attempt 1 for the first attempt, then 2, 3…
ricochet-request-id The ID of the original request at ingest.

A destination can also set, add or remove headers, and send a credential (a bearer token, basic auth or a named header) over HTTPS.

A 2xx answer delivers the event. A 5xx, 408 or 429, a timeout or a connection error is retried; any other answer fails the delivery at once, unless you add its status to the destination’s retry statuses. Retries wait 10 seconds, then three times longer each time (30 s, 90 s…), up to 6 hours, with some jitter; a longer Retry-After is honoured up to a day. By default a delivery gives up after 10 attempts or 3 days, and the destination waits 30 seconds for an answer. All of these are settings on the destination.

After 5 failures in a row the destination’s circuit opens: Ricochet stops sending for 30 seconds, then tries one delivery, and waits longer each time that fails (up to 10 minutes). Deliveries wait while it is open; they are not dropped.

A connection sends one source’s events to one destination. A source can feed several destinations and a destination can take several sources. A connection can have:

  • A filter, so only matching events go through: conditions on headers.<name>, body.<path> or event_type, with equals, one_of, exists, gt, gte, lt, lte and matches (a regular expression), combined with and, or and not.
  • A transformation, a list of steps (rename, drop, add, map) applied to a JSON body before it is sent.
  • A pause: deliveries are held until you resume it.

An event is one stored request: its method, headers, body, sender IP, verification result and event type. The type comes from the provider’s header (X-GitHub-Event, X-Gitlab-Event, X-Shopify-Topic) or the body’s top-level type, event_type or event.

Each event has one delivery per destination it goes to, and each delivery has one or more attempts. A delivery is pending until it succeeds (delivered) or runs out of attempts (dead). The event’s status sums them up: received (no destination), pending, delivered or failed. A failed attempt says what went wrong, in which phase (DNS, connecting, TLS, sending, waiting for the answer) and whose side it is on.

Events are kept for the plan’s retention (7 days during the alpha).

Any stored event can be sent again, as a new delivery:

  • One event, to its original destination or another one, including your CLI destination.
  • Edit and resend: change the body or headers first; it becomes a new event linked to the original, which stays unchanged.
  • Bulk replay: every event that matches a time range and filters (source, status, destination, event type), at a rate you choose, which you can watch, pause and cancel.

Quarantined events are replayed only after you release them.

Without an account, ricochet listen 3000 or Get a test URL on the home page creates a temporary source with a live page. It stores events like any source, with tight limits (100 events, 30 requests a minute, 64 KiB bodies), and expires after 24 hours. Signing up from its live page keeps the URL and its events.

New organizations start on the alpha plan: 50,000 events a month (counted, not refused), 300 ingest requests a minute per source, bodies up to 512 KiB, headers up to 16 KiB, and 7 days of retention.