Skip to content

Webhook events

Every topic your app can subscribe to, the scope it requires, and the envelope every delivery shares.

On this page

Delivery envelope

Delivered for every subscribed topic. Verify X-Dukkan-Hmac-Sha256 (hex HMAC-SHA256 of {X-Dukkan-Timestamp}.{raw body} with the signing secret of the install named in the signed install_id), reject timestamps older than five minutes, deduplicate on the signed envelope id, and refuse a store_id that differs from the install you hold. sequence is strictly increasing per store with gaps — order by it, never count on contiguity. Topics require the matching granted scope (see x-dukkan-topic-scopes); app.uninstalled needs no scope and still delivers after revocation. Respond 2xx with a small body within the timeout; anything else is retried with exponential backoff up to 8 attempts, after which the delivery is dead-lettered and sustained failures disable the subscription. Sandbox rehearsals fired through POST /webhooks/test carry test: true.

Headers

NameTypeDescription
X-Dukkan-Delivery-IdheaderRequiredstring

Delivery attempt id (unsigned; retries reuse it)

X-Dukkan-Install-IdheaderRequiredstring

Unsigned hint of the signed envelope install_id

X-Dukkan-EventheaderRequiredWebhookTopic

Webhook topic. Subscribing needs the read scope that governs the payload; app.uninstalled needs none and still delivers after revocation.

X-Dukkan-TimestampheaderRequiredstring

Unix seconds

X-Dukkan-Api-VersionheaderRequiredstring

Dated v1 revision that produced this payload

X-Dukkan-Hmac-Sha256headerRequiredstring

Request body

NameTypeDescription
idRequiredstring

Event id. Deduplicate on this signed value, never on the delivery header.

api_versionRequired"v1"
topicRequiredWebhookTopic

Webhook topic. Subscribing needs the read scope that governs the payload; app.uninstalled needs none and still delivers after revocation.

Values:order.createdorder.status_changedorder.paidproduct.createdproduct.updatedproduct.deletedinventory.movement_createdfulfillment.requestedfulfillment.createdfulfillment.updatedrefund.createdapp.uninstalled
store_idRequiredstring
install_idRequiredstring

The install this delivery was fanned out to. Verify the signature with THIS install's secret and refuse a store mismatch.

sequenceRequiredinteger

Strictly increasing per store with gaps. Order by it; never count on contiguity.

occurred_atRequiredstring
testboolean

Present and true only for sandbox test deliveries (POST /webhooks/test). Absent in production traffic.

dataRequiredobject
Example
{
  "id": "9b2f6c1e-7d3a-4f0b-9a52-1c8e2d4b6a70",
  "api_version": "v1",
  "topic": "order.created",
  "store_id": "3065a1f2-0c4d-4e8b-b7a9-5d2f8c1e9a44",
  "install_id": "8b2c1d4e-5f60-4a7b-8c9d-0e1f2a3b4c5d",
  "sequence": 1661,
  "occurred_at": "2026-08-18T16:00:00.123Z",
  "data": {
    "order_id": "6f1d2c3b-4a59-4e8f-9b70-2d1c3e4f5a6b",
    "order_number": "1042",
    "status": "placed",
    "total_minor": 275000,
    "currency": "SYP"
  }
}

order.created#

Scope:orders:read

An order is created from any source.

Example payload
{
  "id": "9b2f6c1e-7d3a-4f0b-9a52-1c8e2d4b6a70",
  "api_version": "v1",
  "topic": "order.created",
  "store_id": "3065a1f2-0c4d-4e8b-b7a9-5d2f8c1e9a44",
  "install_id": "8b2c1d4e-5f60-4a7b-8c9d-0e1f2a3b4c5d",
  "sequence": 1661,
  "occurred_at": "2026-08-18T16:00:00.123Z",
  "data": {
    "order_id": "6f1d2c3b-4a59-4e8f-9b70-2d1c3e4f5a6b",
    "order_number": "1042",
    "status": "placed",
    "total_minor": 275000,
    "currency": "SYP"
  }
}

order.status_changed#

Scope:orders:read

Every order status transition.

No example for this topic yet.

order.paid#

Scope:orders:read

Actually-collected funds cross the order total in the payments ledger.

No example for this topic yet.

product.created#

Scope:products:read

A product is created.

No example for this topic yet.

product.updated#

Scope:products:read

A product or one of its variants changes.

No example for this topic yet.

product.deleted#

Scope:products:read

A product is deleted.

No example for this topic yet.

inventory.movement_created#

Scope:inventory:read

A new inventory movement is appended.

Example payload
{
  "movement_id": "0a1b2c3d-0001-4e8f-9b70-2d1c3e4f5a6b",
  "variant_id": "c0ffee00-0001-4e8f-9b70-2d1c3e4f5a6b",
  "location_id": "10c00000-0001-4e8f-9b70-2d1c3e4f5a6b",
  "delta": -2,
  "stock_after": 23,
  "reason": "damage"
}

fulfillment.requested#

Scope:orders:read

A fulfillment is created as pending: a pickup waiting to be booked.

No example for this topic yet.

fulfillment.created#

Scope:orders:read

Any fulfillment is created.

No example for this topic yet.

fulfillment.updated#

Scope:orders:read

A fulfillment status or tracking number changes.

No example for this topic yet.

refund.created#

Scope:orders:read

A refund is recorded.

No example for this topic yet.

app.uninstalled#

Scope:No scope required

The install is revoked; arrives even after tokens are revoked.

No example for this topic yet.