Webhooks

Events

payment.succeeded: funds have been credited to the account. payment.failed: checkout or payment ended unsuccessfully. request.failed: an error was recorded by the gateway (opt in). webhook.test: a test requested by the account owner.

Events are account scoped. Payloads exclude prompts, completions, API keys and supplier credentials. Request events require an entry in the gateway log; failures before logging are outside this event stream.

Verify the signature

Tokely-Signature contains t=timestamp,v1=hex_signature. Compute HMAC-SHA256 using the signing secret as a UTF-8 string over the timestamp, a period, and the exact raw request bytes. Compare signatures in constant time and reject timestamps outside five minutes.

import hashlib, hmac, time

parts = dict(part.split('=', 1) for part in signature.split(','))
assert abs(time.time() - int(parts['t'])) <= 300
expected = hmac.new(secret.encode(), parts['t'].encode() + b'.' + raw_body,
                    hashlib.sha256).hexdigest()
assert hmac.compare_digest(expected, parts['v1'])

Secret rotation takes effect immediately. An attempt already in flight may carry the previous signature. Store the newly shown secret securely.

Delivery and replay

Return any 2xx response within 10 seconds. Redirects are not followed. Non-2xx responses and network errors retry with exponential delay and jitter, starting at about 30 seconds and capped at six hours, up to 12 attempts or 72 hours per delivery cycle. Exhausted deliveries enter the dead-letter state.

Deduplicate effects using the JSON id or Tokely-Event-Id. Delivery is at least once: a lost acknowledgment can cause a duplicate. Replay preserves the original event ID and payload and starts a new delivery cycle. It never recredits payments or reissues inference.

Pausing keeps events queued within the same retry window. The delivery journal preserves attempt timestamps and HTTP response codes. Recipient response bodies are discarded. Deleting an endpoint cancels queued deliveries; a request already in flight may still arrive.

Event envelope

{
  "id": "event-id", "type": "payment.succeeded", "version": 1,
  "account_id": "account-uuid", "occurred_at": "2026-10-07T12:00:00+00:00",
  "resource_id": "payment-order-id",
  "data": { "amount_usd": 25, "status": "credited" }
}

New endpoints receive future events. Test delivery is sent only when you explicitly request it. Budgets, batch and task events are not advertised until their producers exist.