Skip to main content

Overview

Teel sends real-time updates whenever a payout is created or changes status. Two delivery modes: You can use either, or both — they carry the same events.

HTTP webhooks (outbound delivery)

You register one or more URLs, Teel POSTs signed events to them whenever activity occurs on your account.

Register an endpoint

Create a subscription with POST /webhooks/subscriptions (scope webhooks:write), or from the Webhooks page in the dashboard. You provide:
  • url — must be https://. Loopback, RFC1918 / private IPv4 and IPv6 ranges, link-local addresses, and known cloud metadata hosts are rejected at create time and at delivery time (DNS is re-resolved before each POST).
  • events — pick from the allow list (payout.created, payout.status.updated).
  • label — optional; helps you identify the subscription later (never sent to your endpoint).
The response includes a whsec_… signing secret exactly once. Copy it into your verifier’s secret store immediately — there is no way to retrieve it again. Use rotate secret (POST /webhooks/subscriptions/{id}/rotate-secret or the dashboard) if it’s ever lost or you suspect it’s been exposed; the rotation invalidates the old secret immediately (no overlap window).

Account limits

Delivery format

Teel POSTs the JSON envelope to your URL with three headers:

Successful response

Any 2xx response within 10 seconds counts as success. We don’t read the body — return whatever shape you like, including an empty 200. Slow responses are aborted at 10s and treated as a transport failure.

Retry behavior

A non-2xx response, a connection error, or a timeout triggers a retry. The schedule is exponential: After 5 attempts the delivery is marked permanently_failed. It stays visible in the dashboard’s Delivery history indefinitely (until pruned, no sooner than 30 days). Use Replay to re-enqueue a fresh attempt at any time.
Webhook delivery is at-least-once. The same Teel-Delivery-Id may arrive more than once if a 2xx response was lost on the wire. Idempotency on your end (e.g. an INSERT … ON CONFLICT (delivery_id) DO NOTHING) is the safest pattern.

Verifying signatures

Every delivery is HMAC-SHA256-signed over t=<unix>.<raw body> using your subscription’s whsec_… secret. Recompute the digest on your side and constant-time-compare. Reject anything older than 5 minutes — that’s the replay-protection window.
Frameworks that auto-parse JSON (e.g. Express’s default express.json(), FastAPI body parsing) rewrite the body before your handler runs — the bytes you’d then re-serialize won’t match what we signed. Always read the raw body for signature verification, then parse. The samples above use express.raw() and request.get_data() for exactly this reason.

Event types

payout.created

Fires once when a payout is first created (any flow — a Payout, a Stablecoin payout, or a batch row).

payout.status.updated

Fires every time a payout’s status or step changes. Multiple events are emitted per payout as it progresses (pendingprocessingcompleted is at least three updates, plus per-step transitions like compliance_cleared, instructions_sent, collected, converting, settling, delivered).

WebSocket connection

If you can hold an open connection, the WebSocket delivers the same events with sub-100ms latency. Authenticate in-band with your API key as the first message:
The WebSocket scopes events to your account — you only see events for payouts your business owns.

Status transitions

All payouts follow the same status state machine regardless of the underlying provider:

Step transitions

Within processing, the payout progresses through a fixed sequence of steps. The step field on payout.status.updated reflects the current position: initiatedcompliance_clearedinstructions_sentcollectedconvertingsettlingdelivered Steps are monotonic — they only ever move forward.

End-to-end example

A Payout generates roughly this sequence of events (some steps fire multiple times depending on the providers involved):
Your verifier dedupes on Teel-Delivery-Id so transient retries (e.g. one of these events 2xx-acknowledged but the response was lost on the wire) don’t double-process.