ShopEazydevelopers
API referenceStorefront APICheckout

Server-derived totals for a cart

Credentials
  • Store key
Scopes
checkout:write
POST
/v1/store/checkout/quote

Re-derives every line from the live catalogue and computes shipping and totals. Creates nothing. Lines for unknown or inactive items are dropped silently; if none are left the call is 422. A service that takes online bookings needs booking.at. customer and note are accepted and ignored here.

Fulfilment resolves to delivery only when the cart has a shippable line, the store offers delivery and the caller did not ask for collect.

Delivery is priced by the same rule as the hosted checkout: the store's flat fee (free over its threshold), or live courier rates and the merchant's own delivery zones. With a shippingAddress, the quote's delivery lists the options; send the one the shopper picks back as deliveryChoice on the quote and on POST /v1/store/orders. A delivered cart needs a deliverable address (street, suburb, city and postal code): 422 validation_error otherwise. A changed or dearer service is 422 delivery_changed.

Old path, deprecated, sunset 1 Feb 2027: POST /v1/checkout/quote.

Authorization

storeKey checkout:write
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

Request Body

application/json
  1. body
lines*array<>
fulfilment?Fulfilment
Value in"delivery""collect"
shippingAddress?
deliveryChoice?

The delivery service picked from a quote's delivery.options. Never a price to charge: the server re-quotes the courier and charges its price now. If that service is gone, or costs more than expectedPriceMinor, the call is 422 delivery_changed and the shopper chooses again. If no courier can quote at all, the store's flat fee is charged.

customer?
note?string
discountCode?string

Coming soon. The code the shopper typed. Evaluated again on the basket the server prices, and on an order its use is claimed (so the order's Idempotency-Key also protects the redemption). A code that does not apply is dropped and the totals carry no discount; Quote.discount says why. Never a price from the client.

Lengthlength <= 40

Response Body

The quote.

application/json
  1. response
data*
Example Request
curl -X POST 'https://api.shopeazy.co/v1/store/checkout/quote' \  -H 'Authorization: Bearer sez_pub_...' \  -H 'Content-Type: application/json' \  -d '{  "lines": [    {      "itemId": "65myAK8IFZKwZBG84Gmw",      "quantity": 1    }  ],  "fulfilment": "collect",  "customer": {    "name": "Thandi Mokoena",    "email": "thandi.mokoena@example.com",    "phone": "+27 82 000 0001"  }}'
Example Response
{  "data": {    "fulfilment": "collect",    "lines": [      {        "itemId": "65myAK8IFZKwZBG84Gmw",        "variantId": null,        "title": "Monstera deliciosa, 20 cm pot",        "variantTitle": null,        "unitPrice": {          "amount": 32900,          "currency": "ZAR"        },        "customisations": [],        "quantity": 1      }    ],    "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"      }    },    "delivery": null  }}

One published policy's sanitised HTML GET

The body is re-sanitised on the way out, so it is safe to inject. An unpublished (or empty) policy is `404`, not `403`. Old path, deprecated, sunset 1 Feb 2027: `GET /v1/policies/{slug}`.

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`.