Live courier prices for this order
- Credentials
- OAuth access token
- Scopes
orders:read- Idempotency
- Send an
Idempotency-Keyheader. A retry with the same key does the work once.
A POST because it calls the couriers, but changes nothing, so it is not write-gated: a store whose plan has lapsed can still ask for a price.
Access: OAuth scope orders:read; not write-gated.
merchantOAuth orders:readAuthorizationBearer <token>OAuth 2.1 (authorization code + PKCE S256, public clients, dynamic registration) for connected AI apps. Effective power is scopes granted AND the person's role today AND the plan. orders:refund, billing:write and team:write are opt-in on the consent screen. Write implies read for marketing:read and messages:read, so a grant holding marketing:write passes a marketing:read check, and messages:send passes messages:read.
Scope: orders:read
storeId*stringA store the person can reach. Any other id answers 404, never 403.
orderId*stringIdempotency-Key*stringRequired on every write. Minted on the device when the person taps, reused on every retry. A write without one, or with one that does not match the pattern, is refused with 422 validation_error. Scoped to the token's subject, remembered 24 hours. A replay returns the first 2xx response with Idempotent-Replay: true; a request still running answers 409 in_flight; the same key with a different method, path or body answers 409 idempotency_key_reused; non-2xx results are not remembered. If the key store cannot be read nothing is processed and the answer is 503 idempotency_unavailable: retry with the same key.
^[A-Za-z0-9_-]{8,128}$application/json- body
lockerId?stringlockerProvider?stringlockerCourier?stringOK
application/json- response
data*curl -X POST 'https://api.shopeazy.co/v1/merchant/stores/<storeId>/orders/<orderId>/delivery/quote' \ -H 'Authorization: Bearer <access_token>' \ -H 'Idempotency-Key: <unique_key>' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": { "quotes": [ { "provider": "string", "serviceCode": "string", "serviceName": "string", "price": { "amount": 0, "currency": "ZAR" }, "estimate": "string", "dropOff": true, "paidFor": true } ], "paid": { "amount": 0, "currency": "ZAR" }, "parcel": { "totalChargeableKg": 0, "parcels": 0, "usingDefaultFor": [ "string" ], "unmeasurable": true } }}Who is on a tour departure GET
Each party (order, contact, seats, pickup, paid) and each traveller with their answers and whether they were ticked off. Names and answers are the shopper's own words (untrusted text). With no `departure`, the next departure that has bookings; `manifest` is null when nobody is booked on an upcoming one. Access: OAuth scope `orders:read`; not write-gated.
Book a courier collection POST
Re-quotes and books the live quote matching `provider` + `serviceCode`. When that is not what the shopper paid for, answers **409 `confirm_different`** whose `message` is the question to put to the merchant (e.g. "The customer paid R80 for ... Book ... for R95 instead?"); resend with `confirmDifferent: true`. An OAuth connection is held to what the merchant allowed it in Settings, AI apps: a courier costing more than the grant's `courierMaxCents` on the merchant's courier account answers **403 `over_courier_limit`**, and a connection may book 50 a day (UTC) before **429 `rate_limited`**. First-party calls have neither. Access: OAuth scope `delivery:write`; write-gated by the subscription.