Skip to main content
Every webhook event shares the same envelope. This page documents each event, when it fires, and any additional fields in the payload.

Envelope

{
  "event": "<event-name>",
  "order": { /* OrderResource plus per-event extras */ },
  "timestamp": "2026-04-18T10:05:12Z"
}
The order object always contains at least:
FieldTypeExample
idstring (UUID)550e8400-e29b-41d4-a716-446655440000
reference_numberstring | nullREF-2024-001234
order_numberstringORD-2024-0001
typestringearning or replacing
total_pricenumber (SAR)150.75
total_pointsnumber500
payment_statusstringfully_paid or partially_paid
order_statusstringapproved, authorized, captured, cancelled, fully_refunded, partially_refunded, new
metadataobject | null{ "source": "web" }
created_atISO 8601 string2026-04-18T10:00:00Z
updated_atISO 8601 string2026-04-18T10:05:12Z
Headers on every delivery:
HeaderExample
Content-Typeapplication/json
X-Webhook-Secretabc123def456... (the secret you registered)
X-Webhook-EventSame as event in the body

approved

Fires when: an order is fully settled — a paid checkout completes, or an earning order is awarded. Use it to: mark the order as confirmed in your system, send the customer a receipt, trigger fulfilment.
{
  "event": "approved",
  "order": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "reference_number": "REF-2024-001234",
    "order_number": "ORD-2024-0001",
    "type": "replacing",
    "total_price": 150.75,
    "total_points": 500,
    "payment_status": "fully_paid",
    "order_status": "approved",
    "metadata": { "source": "web" },
    "created_at": "2026-04-18T10:00:00Z",
    "updated_at": "2026-04-18T10:05:12Z"
  },
  "timestamp": "2026-04-18T10:05:12Z"
}

authorized

Fires when: funds are authorised on a two-step (auth → capture) payment flow. No money has moved yet. Use it to: reserve inventory, start fulfilment if your flow permits it before capture.
{
  "event": "authorized",
  "order": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "order_number": "ORD-2024-0001",
    "type": "replacing",
    "order_status": "authorized",
    "total_price": 150.75,
    "total_points": 500,
    "payment_status": "fully_paid",
    "created_at": "2026-04-18T10:00:00Z",
    "updated_at": "2026-04-18T10:01:00Z"
  },
  "timestamp": "2026-04-18T10:01:00Z"
}

captured

Fires when: previously-authorised funds are captured. Use it to: finalise the order, release inventory holds, run post-sale automation.
{
  "event": "captured",
  "order": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "order_number": "ORD-2024-0001",
    "order_status": "captured",
    "total_price": 150.75,
    "total_points": 500,
    "payment_status": "fully_paid",
    "created_at": "2026-04-18T10:00:00Z",
    "updated_at": "2026-04-18T10:10:00Z"
  },
  "timestamp": "2026-04-18T10:10:00Z"
}

cancelled

Fires when: an order is cancelled via POST /v1/orders/{uuid}/cancel. Use it to: release inventory, notify the customer, stop fulfilment.
{
  "event": "cancelled",
  "order": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "order_number": "ORD-2024-0001",
    "order_status": "cancelled",
    "total_price": 150.75,
    "total_points": 0,
    "payment_status": "fully_paid",
    "reason": "Customer requested cancellation",
    "created_at": "2026-04-18T10:00:00Z",
    "updated_at": "2026-04-18T10:30:00Z"
  },
  "timestamp": "2026-04-18T10:30:00Z"
}
The reason field, if supplied on the cancel request, is echoed in the payload.

completed

Fires when: a replacing (checkout) order is marked complete via POST /v1/orders/{uuid}/complete — typically after the customer’s payment is confirmed by the PSP. Use it to: close out the order on your side if your flow distinguishes “approved” from “completed”.
{
  "event": "completed",
  "order": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "order_number": "ORD-2024-0001",
    "order_status": "approved",
    "total_price": 150.75,
    "total_points": 500,
    "payment_status": "fully_paid",
    "created_at": "2026-04-18T10:00:00Z",
    "updated_at": "2026-04-18T10:05:12Z"
  },
  "timestamp": "2026-04-18T10:05:12Z"
}

shipping_status_updated

Fires when: you update a physical-goods order’s shipping status via POST /v1/orders/{uuid}/status. Additional fields (inside order):
FieldTypeExample
statusstringOne of new, license_in_progress, ready_shipping, delivery_is_in_progress, delivered, cancelled
status_labelstringHuman-readable label (Arabic/English) shown on the customer’s transaction history
{
  "event": "shipping_status_updated",
  "order": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "order_number": "ORD-2024-0001",
    "order_status": "approved",
    "status": "ready_shipping",
    "status_label": "Ready for shipping",
    "total_price": 150.75,
    "total_points": 500,
    "payment_status": "fully_paid",
    "created_at": "2026-04-18T10:00:00Z",
    "updated_at": "2026-04-18T14:00:00Z"
  },
  "timestamp": "2026-04-18T14:00:00Z"
}

refunded

Fires when: a refund is created through POST /v1/orders/{uuid}/refund. Use it to: reverse fulfilment-side accounting, return value in your ERP, and mark the order as fully or partially refunded on your side. Additional fields (inside order):
FieldTypeExample
refund_amountnumber25.00
reasonstringOne line item returned
typestringearning or replacing
refund_pointsnumber500
left_pointsnumber1500
left_amountnumber75.00
Not every refund payload includes every optional field. For example, refund_points, left_points, and left_amount are especially relevant for earning-order reversals.
{
  "event": "refunded",
  "order": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "order_number": "ORD-2024-0001",
    "type": "earning",
    "order_status": "partially_refunded",
    "total_price": 150.75,
    "total_points": 3000,
    "payment_status": "fully_paid",
    "refund_amount": 25.00,
    "refund_points": 500,
    "left_points": 2500,
    "left_amount": 125.75,
    "reason": "One line item returned",
    "created_at": "2026-04-18T10:00:00Z",
    "updated_at": "2026-04-18T15:00:00Z"
  },
  "timestamp": "2026-04-18T15:00:00Z"
}

auto_refunded

Fires when: Points automatically reverses a replacing order internally, for example when a required transaction record is missing during backend reconciliation. Use it to: treat the order as refund-class terminal state and stop assuming the checkout remained successful. Additional fields (inside order):
FieldTypeExample
refund_amountnumber150.75
refund_pointsnumber3000
reasonstringauto_refund_missing_transaction
typestringreplacing
{
  "event": "auto_refunded",
  "order": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "order_number": "ORD-2024-0001",
    "type": "replacing",
    "order_status": "fully_refunded",
    "total_price": 150.75,
    "total_points": 3000,
    "payment_status": "fully_paid",
    "refund_amount": 150.75,
    "refund_points": 3000,
    "reason": "auto_refund_missing_transaction",
    "created_at": "2026-04-18T10:00:00Z",
    "updated_at": "2026-04-18T16:00:00Z"
  },
  "timestamp": "2026-04-18T16:00:00Z"
}

Testing events

The fastest way to trigger an event end-to-end is to issue the corresponding API call against a sandbox order:
EventAPI call
approvedPOST /v1/orders/earning or complete a checkout
authorizedPOST /v1/orders/{uuid}/authorize
capturedPOST /v1/orders/{uuid}/capture
cancelledPOST /v1/orders/{uuid}/cancel
completedPOST /v1/orders/{uuid}/complete
refundedPOST /v1/orders/{uuid}/refund
auto_refundedInternal event; not normally merchant-triggered
shipping_status_updatedPOST /v1/orders/{uuid}/status

Delivery semantics

  • Points sends webhook deliveries over HTTPS
  • each attempt uses a 10 second HTTP timeout
  • failed deliveries may be retried up to 3 times
  • order is not guaranteed across different events, so your consumer must be idempotent

Next

Handling

Verify the secret, acknowledge correctly, stay idempotent.

Management

Register, update, rotate, and delete webhooks.