Skip to main content
Every Points order moves through a small set of states, driven by either merchant API calls or internal Points events. Understanding the state machine is essential for building a correct refund / fulfilment / reconciliation pipeline. This page is the single source of truth for:
  • the financial state machine (order_status)
  • the shipping status field and its bilingual custom labels
  • which webhook fires on each transition

The state machine

Financial statuses (order_status)

order_status is a string enum in every webhook payload. The OpenAPI spec reflects a legacy numeric field — prefer the string values above.

Two order types

The state machine above applies to both, but earning orders typically move new → approved in a single step with no intermediate authorized / captured.

Shipping status (separate field)

order_status tracks the financial state of the order. Physical-goods orders also have a separate shipping status that you update as you fulfil: Update with POST /v1/orders/{uuid}/status. Each call fires a shipping_status_updated webhook.
Keep shipping status separate from financial status in your own system. order_status tells you whether the order was approved, captured, cancelled, or refunded; status tells you where fulfilment stands.

Custom shipping labels

Different fulfilment platforms (Salla, Zid, custom WMS, …) use status names that don’t map cleanly onto the six built-in shipping stages above. POST /v1/orders/{uuid}/status accepts two optional fields alongside the canonical status enum so you can attach your own bilingual label to an order without us having to extend the enum:
*At least one of status, name_ar, or name_en must be supplied — a fully blank payload returns 422. All three can be sent together; the canonical enum and the custom label update atomically.

Auto-translation

When only one of name_ar / name_en is supplied, the missing locale is auto-filled via Google Translate so custom_status always has both locales. Translations are cached server-side for 30 days keyed on (target, text) — sending the same label across 100 orders only hits Google once. If the translator is unreachable (quota / network), the supplied locale is copied into the missing slot rather than leaving the row half-populated.

Examples

Response fields

Every Order response exposes the resolved label so consumers don’t have to duplicate the fallback logic client-side:
Prefer status_label over custom_status + manual fallback. The label is locale-aware (respects the request’s Accept-Language header / ?lang= parameter) and stays in sync with the resolution logic on our side.

Timeline example — redeem checkout with full refund

  1. Customer clicks Pay → you call POST /orders/checkout/{publicKey} → order is new.
  2. Customer completes payment on business.papp.sa → order transitions to approved, webhook approved fires.
  3. 7 days later, customer requests a refund → you call POST /orders/{uuid}/refund → order moves to fully_refunded, redeemed points are returned, earned points reversed.

Timeline example — authorize-capture flow

  1. POST /orders/checkout/{publicKey} → order is new.
  2. POST /orders/{uuid}/authorize after your PSP authorises → authorized.
  3. POST /orders/{uuid}/capture after you ship → captured.
  4. Later: partial refund for one returned item → POST /orders/{uuid}/refund with amountpartially_refunded.

Invalid transitions

The API enforces the state machine. Examples that return 400:
  • POST /orders/{uuid}/authorize on a cancelled or already-refunded order.
  • POST /orders/{uuid}/capture on an order that is not authorized.
  • POST /orders/{uuid}/refund on an order that has not yet been approved or captured, or outside the allowed refund window.
  • POST /orders/{uuid}/complete on an order that is already approved / captured.
Always check the response message field for the specific reason before retrying.

Webhooks per transition

See Webhook events for payload schemas.

Practical advice

All endpoints take {order:uuid} — the integer id is internal and may change between environments.
Don’t rely solely on webhooks. Run a nightly job that fetches the authoritative state of any order you believe is still live, to catch the rare missed delivery.
A full refund returns redeemed points to the customer and reverses points earned on the refunded portion. Mirror this in your own loyalty calculations if you double-book rewards.
Cancel only while the order is new or authorized. Once funds are captured or the order is approved, use refund instead.

Next

Refunds & cancellations

Rules, windows, and partial refund behaviour.

Webhook events

Per-event payload schemas and sample bodies.