Skip to content

Receive webhooks

Get order updates sent to your server. Answer fast, skip duplicates by event_id, ignore old events, and confirm by polling.

This page shows how to receive webhooks and handle them correctly. You need a public HTTPS URL on your server.

A webhook is an HTTP request that Cardzone sends to your server when an order changes. With webhooks, you don’t have to poll on a timer.

Polling is still the reliable way to get an order’s state. An integration that only polls is correct. Webhooks make it faster.

Where Cardzone sends webhooks

Live orders send webhooks to both of these targets, if you set them:

  1. Your store webhook endpoint. Set it in the Cardzone dashboard (for stores managed in Cardzone). It receives webhooks for every order your API keys create. It can also receive other event types.
  2. A webhook_url for one order. Send it when you create the order. It must be an https:// URL. Its host must not be localhost or a private or reserved IP address. Otherwise create order returns 422 validation_failed, with details set to https_required, host_required, private_host, or invalid_url.

Sandbox API keys never send webhooks. Nothing is sent while your store is inactive.

Events

Each webhook is one event. event_id is the ID of that event.

Event When event_id
b2b.order.created The API accepted an order. This is not a success signal. b2b.order.created:<order_id>
b2b.order.status_changed The public status changed, or Cardzone delivered replacement codes that your store paid for. b2b.order.status_changed:<order_id>:<sequence>

Your store endpoint can also receive events for orders from other Cardzone channels. Those events have a different shape. So:

  • Check event (or the X-Cardzone-Event header) to see which event it is.
  • For any event you don’t handle, answer 200 and do nothing else. Then Cardzone won’t retry it.

Both B2B events use the same outer structure (the envelope). The order inside is the same as the Poll order status response:

{
  "event": "b2b.order.status_changed",
  "event_id": "b2b.order.status_changed:1CBDzqwiovwVrtFk6nZ1Aw:2",
  "platform": "cardzone",
  "source": "b2b_api",
  "created_at": "2026-06-30T09:00:41Z",
  "store": { "id": 42, "uuid": "018f6a1e-…", "name": "Nova Games", "provider": "cardzone" },
  "order": { "order_id": "1CBDzqwiovwVrtFk6nZ1Aw", "status": "fulfilled", "…": "…" }
}

If an order is still processing right after it is created, no status event is sent. The first status_changed event comes with the first real change.

Every delivery is an HTTPS POST request with a JSON body and these headers:

Content-Type: application/json
User-Agent: Cardzone-Webhooks/1.0
X-Cardzone-Event: b2b.order.status_changed
X-Cardzone-Webhook-Id: 9403
X-Cardzone-External-Event-Id: b2b.order.status_changed:1CBDzqwiovwVrtFk6nZ1Aw:2:account_webhook_endpoint:3f9a1c27b4e0d812

Watch a receiver handle webhooks

A receiver is the code on your server that accepts webhooks. Play each sequence to see what a correct receiver does with each delivery. The Retries and out-of-order sequence shows all four outcomes:

Webhook delivery simulatorRuns in your browser · nothing is sent
Sequence:
    Your receiver's state for this order
    Select a delivery

    The four rules

    1. Answer 2xx fast

      A 2xx status (such as 200) means success. Return any 2xx as soon as you have saved the event. Do the real work later, in a background job.

      Anything else counts as a failed delivery:

      • a 3xx (Cardzone does not follow redirects)
      • a 4xx or 5xx
      • a timeout or a connection error

      Cardzone retries a failed delivery up to 3 more times over the next few minutes. Then it drops the delivery. That is why you MUST also reconcile on a schedule. If your host resolves to a private or reserved address, Cardzone refuses the delivery and does not retry.

    2. Skip duplicates by event_id

      The same event can arrive more than once. Save each event_id you have processed. If an event_id arrives again, answer 200 and do nothing.

    3. Ignore old status events

      Deliveries can arrive out of order. The last part of a status_changed event_id is a sequence number. Each order has its own sequence, and the number goes up over time. Skip any event whose sequence number is lower than the highest one you have seen for that order.

    4. Confirm by polling before you move money or codes

      Webhooks are not signed. This means a webhook carries no proof that Cardzone sent it, so anyone could send a fake one to your URL. Treat the webhook body as a hint only. Call GET /api/b2b/v1/orders/{order_id} with your API key, and act on that response. The poll uses your API key, so a fake webhook can’t fake an order’s state.

    Example receiver

    Each example saves the webhook’s event_id. If the event is new, it adds a background job. The job skips older sequence numbers, polls the order, and saves the result.

    Reconcile on a schedule

    To reconcile means to compare your records with Cardzone’s and fix any differences.

    Webhooks can be lost even after retries, for example if your endpoint is down for a while. So run a job every few minutes. The job polls every order that you still have as processing or action_required. Webhooks plus this job keep every order correct.

    Cardzone
    Back to Cardzone

    Type to search…

    ↑↓ navigate↵ selectEsc close