Skip to main content

Event catalog

Every webhook event DZBuild can fire, with the exact shape of its payload data field.

Written by Support

Every webhook payload is wrapped:

{
  "event":       "<event name>",
  "store_id":    13,
  "occurred_at": "2026-04-30T21:18:21+00:00",
  "data":        { /* event-specific, documented below */ },
  "delivery_id": "9f2c41ab77e05d18"
}

This page documents the data field for each event.

Order events

⚠️ Warning — API v1 order events only follow API traffic

An order status event fires only when the status is changed through the API — PATCH /v1/orders/{id} or POST /v1/orders/{id}/cancel. Confirming or shipping from the DZBuild dashboard, bulk send-to-delivery, and automatic courier tracking updates all fire nothing here.

There are also 7 order statuses but only 5 status events: transitions into pending and into processing emit nothing at all. So an order can move pending → processing → shipped and you will see a single order.shipped.

If you need events that cover every order source and every status change, use the merchant Webhooks addon at /dashboard/webhooks instead — see the comparison in the Webhooks overview.

order.created

Fires only for orders created through POST /v1/orders. Storefront checkouts, landing-page orders and manual dashboard orders do not fire this event — wire fulfilment to it and you will miss the overwhelming majority of a merchant's orders.

{
  "data": {
    "order_id":       6894,
    "order_number":   "ORD-13-20260317-AD3C",
    "customer_phone": "0555000000",
    "total":          1000
  }
}

Those four keys are the entire payload. Fetch GET /v1/orders/{id} if you need anything else.

order.confirmed

Fires when status moves to confirmed. Stock is decremented at this moment.

{
  "data": {
    "order_id":   6894,
    "old_status": "pending",
    "new_status": "confirmed"
  }
}

order.shipped

{ "data": { "order_id": 6894, "old_status": "processing", "new_status": "shipped" } }

old_status can be a state you were never told about — the move into processing in this example fired no event of its own.

order.delivered

{ "data": { "order_id": 6894, "old_status": "shipped", "new_status": "delivered" } }

order.cancelled

Fires when an order moves to cancelled from pending, confirmed or processing — those are the only states cancellation is allowed from. POST /v1/orders/{id}/cancel on a shipped or delivered order returns 400 bad_request and no event fires. If the order was already in a committed-stock state, stock is restored before this event fires.

{ "data": { "order_id": 6894, "old_status": "confirmed", "new_status": "cancelled" } }

order.returned

{ "data": { "order_id": 6894, "old_status": "delivered", "new_status": "returned" } }

Payment events

payment.received

Reserved — not currently emitted. The name is accepted in the events array at registration and appears in allowed_events, but nothing fires it. Payment status changes will not reach your endpoint; read GET /v1/orders/{id} if you need it.

Signup / event tracking

signup.counted

Fires when a /v1/signups call lands and was actually counted (not a duplicate).

{
  "data": {
    "key_id":  "dzpub_live_53f32d45fc356",
    "source":  "landing-page-1",
    "country": "DZ"
  }
}

For privacy reasons we do NOT echo back email, phone, or external_user_id in the webhook — your own systems already have those values. The webhook is the "this signup was counted, please mirror it to your CRM" signal.

event.recorded

Reserved — not currently emitted. POST /v1/events records the event and increments usage, but fires no webhook.

Product events

product.stock_low

Reserved — not currently emitted. There is no low-stock push today.

The underlying field is real, though: GET /v1/products/{id} returns low_stock_alert alongside stock_quantity under inventory, so you can poll for the condition yourself.

Internal / test

webhook.test

Fired by POST /v1/webhooks/{id}/test. Lets you check your endpoint is reachable without waiting for a real event.

{ "data": { "ts": 1717112657 } }

It cannot be subscribed to — including webhook.test in the events array at registration returns 400 bad_request "unknown event: webhook.test. Allowed: …". A test is delivered to the webhook you call it on regardless of that webhook's subscription list.

Headers (every event)

Content-Type:    application/json
User-Agent:      dzbuild-webhook/1
X-DZ-Timestamp:  <unix seconds>
X-DZ-Signature:  <hex hmac-sha256>
X-DZ-Delivery-Id: <numeric delivery id>

X-DZ-Delivery-Id is a numeric delivery id (e.g. 4127). It is stable across every retry of that delivery, and it is not the same value as the 16-hex delivery_id in the JSON body.

Versioning

We add new events under v1 freely (additive). When we change the shape of an existing event's data, that's a v2-required change and gets a new path prefix. So your code can rely on:

  • event is stable.

  • New top-level fields may appear in data.

  • Existing field types and meanings won't change without a v2.

  • Order of data keys is not guaranteed.

Did this answer your question?