Valverna

Webhooks · Available now

Delivery & events

Webhooks are how most apps integrate with Valverna. Instead of polling GET /v1/transactions, subscribe once and receive signed HTTPS callbacks the instant a transaction is created on a business that authorized your app.

How delivery works

1 Register a webhook endpoint in your developer dashboard. Subscribe to the event types you care about.
2 When an event fires, Valverna POSTs a JSON payload to your endpoint over HTTPS.
3 Your endpoint verifies the signature, processes the event, and responds with 2xx within 10 seconds.
4 On non-2xx or timeout, Valverna retries with exponential backoff. Delivery is at-least-once — use the eventId for idempotency.

Event types

Subscribe to any subset per endpoint. New event types will be added behind feature flags so existing subscriptions never break.

transaction.created

A new transaction document was ingested from any connected source. Documents are immutable — refunds, credit notes, voids, and exchanges arrive as new linked documents, each firing its own event.

authorization.granted

A merchant approved your app's access request.

authorization.revoked

A merchant revoked your app's access. Stop making requests with that token.

source.connected

The merchant connected a new payment platform (Stripe, Klarna, etc).

source.disconnected

A payment platform connection was removed. Backfill may be incomplete.

Payload shape

Every webhook carries the same envelope — including a top-level schemaVersion string (currently "2.1"). The data field matches the shape of the underlying resource — for transaction events, this is a full canonical transaction.

Example event

{
  "eventId": "evt_7c4b1d9f",
  "schemaVersion": "2.1",
  "type": "transaction.created",
  "businessId": "biz_9f3a2c81",
  "createdAt": "2026-05-12T12:32:20Z",
  "data": {
    "id": "txn_a1b2c3d4",
    "profileId": "se-receipt-1",
    "documentTier": "receipt",
    "type": "sale",
    "settled": true,
    "currency": "SEK",
    "totals": { "subtotal": 28200, "discount": 0, "tax": 3733, "rounding": 0, "total": 28200 },
    "source": { "platform": "izettle", "externalId": "iz_pay_9f8e7d6c",  },
    
  }
}

Verifying signatures

Every request carries a Valverna-Signature header of the form t=<unix>,nonce=<hex>,v1=<hmac>. The v1 value is an HMAC-SHA256, keyed with your signing secret, over the string <t>.<nonce>.<rawBody>. Recompute it and reject any request where it doesn't match.

Request headers

Valverna-Signature: t=1743588842,nonce=9f2a4c8e1b...,v1=c7b3a8e2... Valverna-Event: transaction.created Content-Type: application/json

Reject any request whose t is outside a tolerance window — we recommend 5 minutes — to bound replays. For stricter protection, record the nonce values you've accepted within that window and reject repeats; a retried delivery carries a fresh nonce, so dedupe events on the body's eventId, not the nonce.

Retries & delivery guarantees

Delivery is at-least-once. If your endpoint returns non-2xx or times out (>10s), Valverna retries on an exponential backoff schedule.

Attempt 1

Immediate

Attempt 2

+ 30 seconds

Attempt 3

+ 5 minutes

Attempt 4

+ 30 minutes

Attempt 5

+ 2 hours

Attempt 6

+ 12 hours — final

After six failed attempts, the event is marked failed and stops retrying. Fetch any missed state via GET /v1/transactions — it stays the source of truth for backfill and reconciliation.