Skip to main content
Payouts complete asynchronously, often minutes or days after you create them, so polling is the wrong model. Instead, register an HTTPS endpoint and Infinity sends it an event, as a JSON POST, each time something in your account changes: a payout is paid or returned, a payee becomes active, a deposit is credited.

Register an endpoint

Response
secret is returned only in this response. Store it in your secrets manager; you need it to verify every event delivered to this endpoint. There’s no secret rotation in v1: to replace a secret, create a new endpoint and delete the old one.
  • The URL must use HTTPS and resolve to a public address. IP literals and hosts that resolve to private, loopback or link-local ranges are rejected, as is a URL already used by another of your endpoints.
  • Subscribe to the event types your integration handles. "events": ["*"] subscribes to every type, including types added later.
  • An account can have up to 5 endpoints. Each event is delivered to every enabled endpoint subscribed to its type.

The event object

Every delivery’s body is an event:
data.object is the complete object as it was when the event occurred, in the same shape as the corresponding GET endpoint returns, so most handlers don’t need to call back. The prefix of type identifies the object: payout, batch, payee or deposit. created_at is when the change was committed, not when the event was delivered. environment is live or sandbox.

Verify signatures

Your endpoint is a public URL, so anyone can send requests to it. Verify every event’s signature before acting on it, so that a forged request can’t trigger a fulfilment, a refund or a ledger entry. Infinity signs events with the open Standard Webhooks scheme. Each request carries three headers: The official Standard Webhooks libraries verify the signature and reject timestamps more than 5 minutes from your clock, which protects against replayed requests. Always verify against the raw request body, before JSON parsing; re-serialised JSON won’t match the signature.
  1. Strip the whsec_ prefix from the secret and base64-decode the remainder; that’s the HMAC key.
  2. Build the signed content: {webhook-id}.{webhook-timestamp}.{raw body}.
  3. Compute HMAC-SHA256 over the signed content with the key, and base64-encode the result.
  4. Compare it, using a constant-time comparison, with each v1, entry in webhook-signature. The header can carry several space-separated signatures.
  5. Reject the request if webhook-timestamp is more than 5 minutes from your current time.

Delivery and retries

  • Success is any 2xx response within 10 seconds. Redirects aren’t followed, so a 3xx counts as a failure, as do timeouts and connection errors.
  • Retries. A failed delivery is retried 5 times, after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, about 9 hours in total. After that the delivery is abandoned, and the event remains available through the events API.
  • Signatures are computed per attempt, so webhook-timestamp and webhook-signature change between retries while webhook-id stays the same.
  • Disabled and deleted endpoints receive no new deliveries, and pending retries to them are dropped.

Process events reliably

  • Acknowledge quickly. Return 2xx as soon as the signature is verified and the event is queued, and do the work asynchronously. Slow handlers cause timeouts, which cause retries.
  • Handle duplicates. Delivery is at-least-once, so the same event can arrive more than once. Record processed webhook-id values and skip repeats.
  • Don’t depend on order. Events aren’t guaranteed to arrive in the order they were created; payout.paid can arrive before payout.processing. Use the object’s status to decide what to do, and compare created_at only to avoid applying an older snapshot over a newer one.
  • Ignore unknown types. New event types can be added at any time. Acknowledge them with 2xx and move on.

Recover missed events

Every event is retained for 30 days, whether or not it was delivered and whether or not any endpoint subscribed to it:
After an outage on your side, list events from the start of the outage and process those whose IDs you haven’t recorded. GET /events/{id} returns a single event; its data.object is the snapshot taken when the event occurred, not the object’s current state.

Event types

An object’s creation emits only its .created event; the object’s status in that event tells you where it starts. Each later status change emits <object>.<new status>.

Manage endpoints

Test in the sandbox

Sandbox events carry "environment": "sandbox". To exercise failure handling, force an outcome with the advance helper; for example, move a paid payout to returned: