ShopEazydevelopers
API referenceStorefront APICheckout

An order and its payment status

Credentials, one of
  • Store key and Order access token
  • Store key and Shopper session
  • Store key and Shopper ID token
Scopes
checkout:write
GET
/v1/store/orders/{id}

The id alone is not enough. Send the order's accessToken from POST /v1/store/orders as X-Order-Token, or the shopper who placed it. Neither: 401 order_access_required. A credential for a different order or shopper (or an expired token): 404 not_found, the same answer as an unknown id.

Old path, deprecated, sunset 1 Feb 2027: GET /v1/orders/{id}.

Authorization

headerAuthorizationBearer <sez_pub_... (publishable) or sez_sk_... (secret)>

A per-store key from the console. Publishable keys are browser-safe; secret keys are server-only.

Scope: checkout:write

headerX-Order-Token<token>

The accessToken from POST /v1/store/orders. Reads back that one order for 30 days.

Path Parameters

id*string

The order id returned at placement.

Response Body

The order.

application/json
  1. response
data*
Example Request
curl -X GET 'https://api.shopeazy.co/v1/store/orders/<id>' \  -H 'Authorization: Bearer sez_pub_...' \  -H 'X-Order-Token: <order_access_token>'
Example Response
{  "data": {    "id": "riEhGrBxE2kF04RzXyaz",    "number": "ORD-00001",    "status": "open",    "paymentStatus": "unpaid",    "fulfillmentStatus": "unfulfilled",    "currency": "ZAR",    "totals": {      "subtotal": {        "amount": 32900,        "currency": "ZAR"      },      "discount": {        "amount": 0,        "currency": "ZAR"      },      "shipping": {        "amount": 0,        "currency": "ZAR"      },      "tax": {        "amount": 0,        "currency": "ZAR"      },      "total": {        "amount": 32900,        "currency": "ZAR"      },      "paid": {        "amount": 0,        "currency": "ZAR"      },      "balanceDue": {        "amount": 32900,        "currency": "ZAR"      }    },    "customer": {      "name": "Thandi Mokoena",      "email": "thandi.mokoena@example.com",      "phone": "+27 82 000 0001"    },    "lines": [      {        "itemId": "65myAK8IFZKwZBG84Gmw",        "title": "Monstera deliciosa, 20 cm pot",        "variantTitle": null,        "quantity": 1,        "unitPrice": {          "amount": 32900,          "currency": "ZAR"        },        "customisations": [],        "fulfillmentStatus": "unfulfilled",        "booking": null      }    ],    "createdAt": 1792924396814,    "paidAt": null  }}

Place an order POST

Re-derives prices server-side, finds or creates the customer, and reserves booking slots atomically. `customer.name` is required. **Members-only stores** (`store.membership.enabled`): a shopper credential is required (`401 shopper_required` without one) and the shopper must be an active member (`403 not_a_member`). On other stores a shopper credential is optional; when sent, the order is linked to that shopper (it appears in `GET /v1/store/me/orders` and they can read it back with their credential). The response carries **`accessToken`**, returned only here. Keep it for the confirmation page: `GET /v1/store/orders/{id}` and `/confirm` need it as `X-Order-Token` (or the shopper who placed the order). The id alone reads nothing. **Coming soon (the fields are in the schema and say so).** `discountCode`, `paymentMethod` (`eft` answers bank details), event `attendees`, a price guard (`expectedUnitMinor`), a whole-basket stock check, `giftNote`, `marketingOptIn`, `saveAddress`, `attribution`, `basketId`. The refusals reuse `422 validation_error` with `details.reason` (`price_changed`, `items_unavailable` with `details.unavailableItemIds`, `out_of_stock`, `discount_invalid`, `payment_method_unavailable`); a stale delivery stays `422 delivery_changed`. When sent, `customer.email` is checked and lowercased and `customer.phone` is normalised to E.164 (`422 validation_error` names the field); `customer.name` stays the only required field, because requiring more would break existing sites. Old path, deprecated, sunset 1 Feb 2027: `POST /v1/orders`.

Open a hosted checkout for the order's balance POST

Goes through whichever provider the merchant activated (Yoco, iKhokha or Peach). Send the shopper to the returned `url`. Return URLs must be absolute and `https` (`http://localhost` is allowed for development). Peach renders its form inside your own page, so pass `checkoutPageUrl` when the store is on Peach. The checkout is opened with your site's origin (from `checkoutPageUrl`, else `successUrl`); for Peach that domain must be on the merchant's allowlist. Settlement is the platform's job: iKhokha and Peach call back to this API, Yoco to ShopEazy's own webhook. Use `POST /v1/store/orders/{id}/confirm` on your success page. Old path, deprecated, sunset 1 Feb 2027: `POST /v1/orders/{id}/pay`.