Orders
Orders are created on Quiqqy and pushed to you; you report progress back. Quiqqy owns the customer, payment and (for delivery) the courier — your POS owns the kitchen. The four calls below are everything you send.
The lifecycle ladder
Section titled “The lifecycle ladder”| Step | Order status | Moved by | Your POS action |
|---|---|---|---|
| Created | pending | Quiqqy — you receive order.created | Accept or deny within the SLA |
| Accepted | confirmed | You — POST /pos/orders/:orderId/accept | Optionally send estimated_prep_time_minutes |
| Preparing | preparing | You — POST /pos/orders/:orderId/status | When the kitchen starts |
| Ready | ready | You — POST /pos/orders/:orderId/ready | Food is ready for pickup / courier handoff |
| Out for delivery | out_for_delivery | Quiqqy (courier flows) or you (self-delivery, via status) | Delivery orders only; pickup ends at ready |
| Completed | delivered | Quiqqy — you receive order.delivered | Close the ticket |
| Cancelled | cancelled | Either side — you via cancel, Quiqqy via order.cancelled | Void the ticket, release stock |
| Failed | failed | Quiqqy | Injection or payment failure; nothing to do |
Transitions only move down the ladder. Skipping a rung (confirmed straight
to ready) is allowed; moving backwards returns
409 order.invalid_transition.
SLA guidance
Section titled “SLA guidance”- Acknowledge the webhook in under 10 seconds — that is the delivery timeout, and slow acknowledgements cause duplicate deliveries.
- Accept or deny within 5 minutes of
order.created. Past that, the customer sees a delay warning and Quiqqy support may cancel on your behalf. Both outcomes count against your quality metrics. - Send prep time when the POS knows it.
estimated_prep_time_minutesdrives the customer’s ETA and courier dispatch timing. An accurate 25 beats a silent default.
If the integration has auto_accept_orders: true, Quiqqy confirms each order
the moment it is created — you still receive order.created and drive the
rest of the ladder.
Accept
Section titled “Accept”curl -s https://auth.quiqqy.ai/api/v2/pos/orders/ord_9d31f2/accept \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "estimated_prep_time_minutes": 25 }'await fetch('https://auth.quiqqy.ai/api/v2/pos/orders/ord_9d31f2/accept', { method: 'POST', headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ estimated_prep_time_minutes: 25 }),});import requests
requests.post( "https://auth.quiqqy.ai/api/v2/pos/orders/ord_9d31f2/accept", headers={"Authorization": f"Bearer {access_token}"}, json={"estimated_prep_time_minutes": 25},){ "status": "success", "order_id": "ord_9d31f2", "order_status": "confirmed", "estimated_prep_time_minutes": 25}Progress
Section titled “Progress”POST /pos/orders/:orderId/status with the new status and an optional
customer-visible message:
{ "status": "preparing", "message": "In the oven" }status accepts pending, confirmed, preparing, ready,
out_for_delivery, delivered, cancelled, failed — in practice you send
preparing, and out_for_delivery/delivered only for self-delivery.
POST /pos/orders/:orderId/ready is the no-body shortcut for the most common
transition — the KDS bump:
curl -s -X POST https://auth.quiqqy.ai/api/v2/pos/orders/ord_9d31f2/ready \ -H "Authorization: Bearer $ACCESS_TOKEN"Deny with a structured reason
Section titled “Deny with a structured reason”When the order cannot be fulfilled, cancel it with a free-text reason (the
customer sees it) and a structured reason_code from the
denial enum — ITEM_OUT_OF_STOCK, STORE_CLOSED,
POS_OFFLINE, CAPACITY, PRICE_MISMATCH, OTHER:
curl -s https://auth.quiqqy.ai/api/v2/pos/orders/ord_9d31f2/cancel \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "reason": "Burrata is finished for the day", "reason_code": "ITEM_OUT_OF_STOCK" }'await fetch('https://auth.quiqqy.ai/api/v2/pos/orders/ord_9d31f2/cancel', { method: 'POST', headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ reason: 'Burrata is finished for the day', reason_code: 'ITEM_OUT_OF_STOCK', }),});import requests
requests.post( "https://auth.quiqqy.ai/api/v2/pos/orders/ord_9d31f2/cancel", headers={"Authorization": f"Bearer {access_token}"}, json={ "reason": "Burrata is finished for the day", "reason_code": "ITEM_OUT_OF_STOCK", },)The customer is refunded automatically, and a structured reason_code lets
Quiqqy remediate — ITEM_OUT_OF_STOCK prompts an availability resync so the
next customer never sees the dead items. The best denial, though, is the one
that never happens: keep
availability current.
Mapping order lines
Section titled “Mapping order lines”Each line in an order payload carries the external_id you pushed at menu
sync (and variant_external_id for SKU lines, and external_id per selected
option). Ring up orders off those ids — never off product_name or Quiqqy’s
internal product_id. Ids are scoped to your app: lines synced by another
integration, or never pushed from your system, arrive with
external_id: null — fall back to product_name for those. Full payloads:
Webhook events and
Sample payloads.