Skip to main content

Delivery and retries

Once a webhook is configured, Straumur delivers every matching event to your endpoint as an HTTP POST with a JSON body. This page describes how that delivery behaves: what we send, what we expect back, and what happens when your endpoint does not answer.

The request we send

Each delivery is a POST to the URL registered on the webhook, with the event as a JSON body.

POST /your/webhook HTTP/1.1
Content-Type: application/json; charset=utf-8
Authorization: 5c1f…the webhook API key
X-Webhook-CorrelationId: 8f3c1b2e-4a91-4d5e-9b17-2c6f0a3d8e42

{ "checkoutReference": "…", "payfacReference": "…", "hmacSignature": "…", … }
HeaderDescription
AuthorizationThe webhook API key issued when the webhook was registered, sent as the raw value — there is no Bearer prefix. Compare it against the key you stored.
X-Webhook-CorrelationIdA unique identifier for this delivery attempt. A retry of the same event carries a new value. Log it — it is what we use to trace a specific delivery for you.
Content-TypeAlways application/json; charset=utf-8.
info

The Authorization header authenticates the caller — it tells you the request really came from Straumur. It is not the secret used to verify the hmacSignature field in the body; that is the separate HMAC key, also issued when the webhook was registered. Validate both.

Responding

Your endpoint must answer with HTTP 200 within 15 seconds.

warning

200 is the only response we treat as success. Any other status code — including 201, 202 and 204 — is recorded as a failed attempt and starts the retry sequence, as are timeouts, connection errors and TLS failures.

The 15-second limit covers your entire response, not just the connection. If your handler does real work before replying — writing to a database, calling another service, sending an email — that work counts against the budget. Acknowledge the event first and process it asynchronously.

Retry schedule

If an attempt does not return 200, we retry on a fixed, increasing schedule. There are five attempts in total.

AttemptSentElapsed since the event
1Immediately
2+ 1 minute1 minute
3+ 10 minutes11 minutes
4+ 1 hour~1 hour
5+ 6 hours~7 hours

After the fifth attempt fails, the delivery is marked as failed and is not retried again. The whole sequence spans a little over seven hours, which is the window you have to restore a broken endpoint before events are lost.

info

Every attempt is recorded, including the response status code and body we received. If you believe an event was never delivered, contact us with the X-Webhook-CorrelationId — or the payfacReference from the event — and we can tell you what your endpoint returned.

What to build for

Expect the same event more than once

A retry delivers the same event again, and your endpoint may have processed the earlier attempt successfully before timing out. Make your handler idempotent: key on the identifiers in the event rather than assuming each delivery is new.

Duplicates also arise legitimately. If two of your webhooks both cover the same contract and both subscribe to the event type, each one receives its own delivery. Partners see this too — a payment on a merchant contract can reach both the merchant's own webhook and the partner webhook whose agreement that contract is linked to.

Do not depend on ordering

Deliveries are independent jobs, and a retried event can arrive after a later one. Treat each event on its own merits rather than assuming it follows the previous one.

Answer first, work later

The pattern that survives load is: validate the Authorization header, verify the hmacSignature, put the event on your own queue, return 200. Everything else happens after the response.

Checklist

  • Endpoint reachable over https, on a complete absolute URL
  • Returns 200 — and nothing else — well inside 15 seconds
  • Validates the Authorization header against the stored webhook API key
  • Recomputes and compares hmacSignature using the HMAC key
  • Handles duplicate deliveries idempotently
  • Logs X-Webhook-CorrelationId with every request