POST request at your configured endpoint the moment an event fires.
Webhook endpoints (URL, event subscriptions, and signing secrets) are managed through the Cleo Pay dashboard. Your signing secret is shown once — when the endpoint is created or the secret is rotated — so store it securely.
How webhooks work
When a subscribed event occurs, Cleo Pay sends an HTTPPOST request with a JSON body to your endpoint. Every delivery is signed — see Verifying deliveries. Your endpoint must respond with any 2xx status code within 10 seconds to acknowledge receipt; the response body is ignored.
Payload structure
Every delivery uses the same JSON envelope:Example delivery
Payloads are thin references — they never contain a resource snapshot. Fetch the resource’s current state from the API using
data.id (for example, GET /v1/payables/{id}). A delivery that arrives late or out of order still leads you to the latest state.sequence (or createdAt), never by arrival.
Event types
Subscribe to individual event types, or to the wildcard* to receive all of them.
Payable events
Bank account events
Contact events
Delivery headers
Every delivery includes these HTTP headers:Verifying deliveries
Every delivery is signed with HMAC-SHA256 so you can confirm it came from Cleo Pay and was not tampered with in transit. TheCleo-Signature header carries a Unix timestamp and a hex-encoded signature:
{t}.{raw request body} — the timestamp, a literal dot, then the exact body bytes — keyed with your endpoint’s signing secret (whsec_…).
1
Capture the raw request body
Verify against the exact bytes Cleo Pay sent. Do not JSON-parse and re-serialize the body first — any change in key order or whitespace invalidates the signature.
2
Parse the Cleo-Signature header
Extract
t (Unix timestamp in seconds) and v1 (hex-encoded signature).3
Reject stale timestamps
If
t is more than 5 minutes from the current time, reject the request. This bounds replay attacks. The timestamp is computed fresh for every delivery attempt, so legitimate retries always carry a current t.4
Recompute and compare
Compute the HMAC-SHA256 of
{t}.{rawBody} with your signing secret and compare it to v1 using a constant-time comparison.Test your implementation
Use this known-good vector to unit-test the HMAC computation (bypass the timestamp-freshness check for the fixedt):
Signing secrets
- Each endpoint has its own secret, prefixed with
whsec_(32 bytes of entropy). - The full secret is shown once, when the endpoint is created or its secret is rotated. Only the last 4 characters are visible afterwards.
- Rotation takes effect immediately, with no overlap window. Deliveries already in flight at rotation time were signed with the old secret and may fail your verification — redeliver them from the dashboard if needed.
Retry behavior
A delivery succeeds only when your endpoint returns a2xx status within 10 seconds. Anything else — a timeout, a connection failure, or a non-2xx response — is retried:
- Up to 10 attempts per delivery.
- Exponential backoff starting at 60 seconds and doubling each attempt (1m, 2m, 4m, …), spanning roughly 8.5 hours in total.
- The body and event
idare identical on every attempt; the signature timestamp is computed fresh per attempt.
Automatic pausing
An endpoint is automatically paused after 20 consecutive failed deliveries, or after failing continuously for 24 hours — whichever comes first. When that happens:- The endpoint stops receiving deliveries and shows as Paused in the dashboard.
- Your business admins are notified by email.
- Once your endpoint is fixed, re-enable it from the dashboard and redeliver anything you missed from the delivery log.
id and body — your dedupe logic treats it as the same event — but carries a new Cleo-Webhook-Id and a freshly computed signature.
Endpoint requirements
- HTTPS only —
http://URLs are rejected. - The URL must resolve to a publicly routable address. Localhost, private-network, and internal addresses are rejected.
- Redirects are not followed — the endpoint must respond directly.
- Respond within 10 seconds — acknowledge first, process asynchronously.
Best practices
Verify every request
Reject anything that fails signature verification before touching the payload. Your webhook URL is reachable by anyone on the internet — the signature is what makes a request trustworthy.
Deduplicate on the event ID
Retries and redeliveries reuse the same event
id. Store processed IDs and skip duplicates instead of processing them twice.Respond fast, process async
Return
2xx immediately and hand the event to a queue or background job. Slow handlers hit the 10-second timeout and trigger retries.Don't rely on delivery order
Events can arrive out of order. Order by
sequence, and fetch the resource’s current state from the API rather than reconstructing it from events.