Skip to main content

Webhooks

Clara delivers webhook callbacks when card transaction events occur. You register a subscription — a callback URL, the transaction statuses you care about, and a signing secret — and Clara POSTs the event to your URL, signed with HMAC-SHA256.
Webhooks v1 (/api/v1/subscribers) is no longer available. See how to migrate to v3.

How it works

  1. Create a subscription with POST /api/v3/webhooks/subscriptions.
  2. When a matching transaction event occurs, Clara signs the exact JSON bytes and POSTs them to your callbackUrl.
  3. Verify the X-Clara-Signature header before processing.
  4. Deduplicate by event, then return 2xx quickly. Transient failures are retried (see Retries).

Events

Webhooks are scoped to one entity, CARD_TRANSACTIONS, with these statuses: Subscribe only to the statuses you need: NOTIFICATION, PRE_AUTHORIZED and AUTHORIZED fire for every card purchase, and a single transaction usually goes through several of them, so they produce far more deliveries than the other statuses.

Payload

The body is the transaction event exactly as Clara’s transactions service publishes it: Clara does not wrap, filter or re-serialize it. Top-level fields:
How each subscribed status appears in the body: A refund is notified once, as REFUNDED, when it is finalized (status AUTHORIZED). Its earlier stages are not notified, and a subscription to AUTHORIZED alone does not receive refunds. A rejected or deleted refund is notified as REJECTED or DELETED.
Billing fields (for example billable and the billing dates) are filled after the event is emitted, so they are usually null in webhook payloads. Fetch the transaction from the Transactions API when you need them.
  • data.operationInfo.operationDate is the transaction’s creation time. There is no last-updated timestamp.
  • data.user is the cardholder.
  • data.labels and data.comment are not populated yet: expect them empty or absent.
  • data.invoice and data.invoiceTypeDescription are the bank concept of the movement; use type to tell purchases, refunds, fees, credits and payments apart.
DELETED events are emitted when a transaction is cleaned up and can carry fewer data fields than other events. data.uuid and data.companyUUID are always present.

Delivery

Each delivery is a POST to your callbackUrl with: The body is the raw JSON event. Do not re-serialize it before verifying the signature. Your endpoint must:
  • Be a public https URL. Private, loopback, link-local and cloud-metadata addresses are rejected when you create the subscription and again before every delivery.
  • Answer directly: redirects are not followed, and a 3xx counts as a failed delivery.
  • Accept the connection within 3 seconds and respond within 10 seconds. Acknowledge with 2xx first and process asynchronously.

Verifying the signature

The signature is HMAC-SHA256(secret, t + "." + rawBody), where secret is the subscription secret you provided, t is the t= value from the header, and rawBody is the exact request body.
Verify against the raw request body exactly as received. Parsing and re-serializing the JSON changes the bytes and breaks the signature.
Before processing a delivery:
  1. Verify the signature over the raw body.
  2. Reject timestamps older than your tolerance (5 minutes is recommended) to block replays.
  3. Deduplicate (see Delivery guarantees).

Delivery guarantees

Delivery is at-least-once. Clara does not deduplicate on your behalf: retries, resends and rare races can deliver the same event more than once. Store a key per processed event and ignore repeats. Use data.uuid together with the subscribed status (for example 6f1d0c2e-…:REJECTED): data.uuid identifies the transaction, which can produce more than one event.

Retries

Retries wait about 1 minute, 5 minutes, 15 minutes, 1 hour, 4 hours, 8 hours and 12 hours. A delivery is abandoned after 8 attempts or 48 hours, whichever comes first. Use resend to recover events after a long outage.

Circuit breaking

A circuit breaker per company and entity opens after repeated failures, so a failing endpoint does not receive a burst of requests. While it is open, deliveries are postponed and retried after the cooldown instead of being sent.

Sandbox

Test webhooks end to end without production data:
  • POST /api-test/v3/webhooks/subscriptions — register a sandbox subscription (environment = SANDBOX).
  • POST /api-test/v3/webhooks/events — submit a transaction event in the payload format. Clara delivers the exact bytes you submit, signed like a production delivery, synchronously to your enabled sandbox subscriptions, and returns the results.
See Sandbox for the full contract.

Resend stored events

Re-deliver stored events of your company within a date range to your current enabled subscriptions — not the subscription that originally received them, which may have been deleted. Useful to recover events after a callback outage. POST /api/v3/webhooks/{entity}/resend — scope write:subscribers. entity is CARD_TRANSACTIONS.
202 Accepted — events are queued and delivered asynchronously (paced), so the counts describe queued work, not deliveries that already reached the subscriber:
  • deliveriesQueued is the number of attempts submitted (one per current enabled subscription).
  • When the window holds more than 1000 events, only the oldest are queued and truncated is true; call again with after = nextCursor to continue.
  • Errors: 400 (missing dateFrom/dateTo, or a malformed cursor), 422 (unsupported entity, or a range wider than 90 days).
Delivery is at-least-once: deduplicate as described in Delivery guarantees. A resend shares the per-company/entity circuit breaker with live deliveries.

Next steps