Void a sale rung up at the till by mistake
- Credentials, one of
- Station token
- OAuth access token
- Scopes
orders:refund- Subscription
- Needs an active plan. Otherwise
403 subscription_locked. - Idempotency
- Send an
Idempotency-Keyheader. A retry with the same key does the work once.
Moves money. A full refund with the stock put back (the lines already handed over too: the goods come back across the counter), posted to the ledger like any refund: the same VAT, platform-fee and stock rules as refundOrder, which it runs. The Idempotency-Key is the refund's own key, so a retry books the money back once. Already voided is not an error: the order comes back with alreadyVoided: true.
Only a till sale (channel manual), only within a day of being rung up, and only money taken at the counter (Cash, Card, EFT, SnapScan, Zapper, Other): a sale paid through the card partner (Scan to pay) is refunded with refundOrder, where the partner is asked to pay it back. Otherwise 422 validation_error with the sentence. An order that is not paid is 422.
The order gets the note "Voided at the till" and the call joins the Activity log. No email goes to the customer, as at the console's till.
Till PINs: when the store uses them, the cashier is the one cashierTicket names (403 cashier_required without one).
Access: OAuth scope orders:refund (opt-in on the consent screen, owner/admin grants only, never in a preset); write-gated by the subscription. Unlike refundOrder a staff member can void at the till, as in the console.
AuthorizationBearer <token>It may call only the operations whose x-stations lists its kind, on its own store, and acts through the access of the owner or admin who paired it. Unpairing it, or that person leaving the business, ends it (401 station_revoked).
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
cashierTicket?stringNeeded when the store uses till PINs.
OK
application/json- response
data*curl -X POST 'https://api.shopeazy.co/v1/merchant/stores/<storeId>/orders/<orderId>/void' \ -H 'Authorization: Bearer <access_token>' \ -H 'Idempotency-Key: <unique_key>' \ -H 'Content-Type: application/json' \ -d '{}'{ "data": { "id": "string", "number": "string", "channel": "online", "customerName": "string", "soldByName": "string", "total": { "amount": 0, "currency": "ZAR" }, "status": "open", "paymentStatus": "unpaid", "fulfillmentStatus": "unfulfilled", "placedAt": 0, "lineCount": 0, "kinds": [ "physical" ], "currency": "string", "customerEmail": "string", "customerPhone": "string", "lines": [ { "id": "string", "kind": "physical", "requiresShipping": true, "title": "string", "variantTitle": "string", "customisations": [ { "groupName": "string", "choiceName": "string", "price": { "amount": 0, "currency": "ZAR" } } ], "quantity": 0, "unitPrice": { "amount": 0, "currency": "ZAR" }, "lineTotal": { "amount": 0, "currency": "ZAR" }, "fulfilled": true, "imageUrl": "string" } ], "subtotal": { "amount": 0, "currency": "ZAR" }, "shipping": { "amount": 0, "currency": "ZAR" }, "discount": { "amount": 0, "currency": "ZAR" }, "paid": { "amount": 0, "currency": "ZAR" }, "refunded": { "amount": 0, "currency": "ZAR" }, "kept": { "amount": 0, "currency": "ZAR" }, "refundable": { "amount": 0, "currency": "ZAR" }, "pendingRefunds": [ { "amount": { "amount": 0, "currency": "ZAR" }, "providerName": "string" } ], "eft": { "reference": "string", "overdue": true, "overdueAt": 0, "bankName": "string", "account": "string" }, "customerNote": "string", "giftNote": "string", "notes": [ { "at": 0, "message": "string", "author": "string" } ], "timeline": [ { "at": 0, "type": "created", "message": "string", "author": "string" } ], "fulfilment": { "method": "delivery", "serviceName": "string", "lockerName": "string", "address": "string", "provider": "string", "serviceCode": "string", "locker": { "id": "string", "name": "string", "provider": "string", "courier": "string", "address": "string", "hours": "string" }, "fellBack": true }, "shippingAddress": "string", "shippingAddressParts": { "addressLine": "string", "localArea": "string", "city": "string", "province": "string", "postalCode": "string" }, "shippingAddressMissing": [ "string" ], "needsCourier": true, "shipment": { "provider": "string", "trackingReference": "string", "state": "pending", "estimate": "string" }, "alreadyVoided": true }}Close the register by counting the drawer POST
Freezes the shift's takings by payment method (net of refunds) and what the drawer should hold (the float plus every cash payment taken while it was open), beside `countedMinor`. The difference is over or short. A shift that is gone or already closed is 422 `validation_error`. The call joins the Activity log, with the expected and counted cash. Till PINs: as `openRegister`, a call without a valid `cashierTicket` is 403 `cashier_required` when the store uses PINs. Access: OAuth scope `orders:write`; write-gated by the subscription.
Start a Scan to pay sale POST
The customer pays on their own phone. The sale is priced from the catalogue as the till prices it (a keyed-in `custom` line, a price agreed at the till and the shelf check included), made as an UNPAID order, and the answer is the link for its pay page to show as a QR. It charges through the merchant's own card partner (Yoco, iKhokha or Peach: the first connected one the storefront charges through); the link carries a pass for this one order and works with the online shop switched off. Nothing is charged by this call: the partner confirms the payment like any online one, so VAT, the platform fee and stock follow the online rules. Poll `getScanToPayStatus`. The `Idempotency-Key` makes a retry return the first answer, so one tap makes one order and one link. No card partner connected is 422 `validation_error`; an item that shows out of stock is 409 `out_of_stock` (send `allowOversell` to sell it anyway). An order the customer never pays is cancelled with `cancelOrder`; a paid one is handed over with `fulfilOrder`. Till PINs: when the store uses them, the cashier is the one `cashierTicket` names (403 `cashier_required` without one). Access: OAuth scope `orders:write`; write-gated by the subscription.