Skip to main content
Outbound webhooks are how Caratuva tells your ERP about every state change on a payment intent — buyer KYC approved, transfer confirmed, settled, failed. You register a URL and an event list once, then verify the HMAC signature on each delivery.

Subscribe

Response (the secret field is the only place the signing secret ever appears):
Store secret immediately — Caratuva encrypts it at rest and cannot return it again. If you lose it, rotate it.

Event types

Two parallel namespaces:
  • payment_intent.* — fires only for invoices created via POST /v1/payments. Subscribe to these if you’re a B2B integrator. You will not see noise from any dashboard activity.
  • invoice.* — fires for every invoice (both API-created and dashboard-created). Subscribe to these only if you also operate the dashboard.
The full list of payment_intent.* events:

Delivery format

Endpoints must respond with a 2xx status within 10 seconds. Anything else is treated as a delivery failure.

Signature verification

The signature header is t=<unix_ts>,v1=<sha256_hex>. The signed payload is ${t}.${rawBody} (the raw HTTP body bytes, not a re-serialized JSON object). Always verify against the raw bytes — re-serializing changes whitespace and key order and breaks the MAC.

Steps

  1. Parse t and v1 from the X-Caratuva-Signature header.
  2. Reject the request if |now - t| > 300 (5-minute replay window).
  3. Compute expected = HMAC-SHA256(secret, "${t}.${rawBody}") and compare hex-encoded against v1 with a constant-time comparator.
  4. Use X-Caratuva-Delivery-Id as an idempotency key on your side — replays with the same id should be no-ops.

Node.js example

Python example

Retries and backoff

Caratuva attempts immediate delivery, then retries on any non-2xx or transport error with exponential backoff: After the seventh attempt the delivery flips to dead_letter and Caratuva stops retrying. Use GET /v1/webhooks/:id/deliveries to inspect failed deliveries and replay them manually if needed.

List subscriptions

Inspect deliveries

Each row carries attempt, status (pending | delivered | failed | dead_letter), the upstream responseStatus, and the next nextAttemptAt. In practice the values you’ll see today are pending, delivered, and dead_letter — a row stays pending between retries and flips to dead_letter after the final attempt; failed is reserved.

Rotate the secret

The response carries the new secret exactly once. Rotation is immediate and atomic — the API stores only one secret per subscription, so the old secret stops verifying on the very next delivery and there is no dual-secret overlap window. Deploy the new secret to your verifier first (or atomically with the rotate call); any in-flight or retried deliveries are re-signed with the new secret when they’re sent. Don’t configure your verifier to accept “either” secret expecting an overlap — there isn’t one.

Delete (deactivate)

Soft-deletes the subscription (active=false). No further deliveries fire; historical deliveries rows remain queryable.