Skip to main content
If you supply notification_url on POST /v1/payouts, the provider POSTs a signed event to that URL on every status transition — PENDING, PROCESSING, PAID, REJECTED, CANCELLED. No polling required.
Sandbox mock webhooks are fire-and-forget. A non-2xx response or a timeout is logged at WARNING and not retried. The production enviroment will add at-least-once delivery with retries; for now, treat webhooks as a “nice push hint” and back them with GET /v1/payouts/{id} polling for correctness.

Subscribing

Pass notification_url on create. HTTPS only — http:// URLs are rejected with 422 VALIDATION_ERROR.
The webhook fires on every transition the payout goes through, so a happy-path payout produces three events: PENDING, PROCESSING, PAID.

Event shape

data is the full Payout resource after the transition — same shape as GET /v1/payouts/{id}. Use data.status and previous_status to drive your handler.

Signature verification

Every webhook is signed with HMAC-SHA256 over the raw response body, using your webhook secret. The signature is sent in the X-Clevis-Signature header as:
Verify the signature before trusting any field on the event. Use a constant-time comparison (hmac.compare_digest in Python, crypto.timingSafeEqual in Node.js) to avoid timing attacks.
Compute the HMAC over the raw body bytes, not the parsed-and-restringified JSON. In Node.js with Express that means express.raw(); in Python with FastAPI, await request.body() before reading JSON.

Delivery semantics in v1

Always back your webhook handler with GET /v1/payouts/{id} reconciliation on a timer — in v1 because there are no retries, and in the long term to handle out-of-order delivery and missed events from any push system.

Mock-only knobs

  • processing.step_seconds: 0 on create — no webhooks fire, because all transitions are applied synchronously before the create response is returned. The terminal state is already in the response body.
  • processing.step_seconds: 30 (or any non-zero value) — webhooks fire on every transition, separated by that many seconds.

Testing locally

A common pattern: point notification_url at an ngrok or Cloudflare Tunnel URL running on your machine, and use a MOCK_SLOW_ external_id to make the transitions visible at human pace.