Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.papp.sa/llms.txt

Use this file to discover all available pages before exploring further.

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

Overview

How delivery works, secret verification, and the recommended consumer pattern.

Management

Register, update, rotate, and delete webhooks.