Jump to an endpoint
- GET/
locations/ {locationId} - GET/
locations/ {locationId}/ menu - GET/
locations/ {locationId}/ menu/ items/ {itemId} - GET/
locations/ {locationId}/ schedule - POST/
locations/ {locationId}/ delivery/ quote - POST/
orders/ quote - POST/
orders - GET/
orders/ {orderId} - POST/
orders/ {orderId}/ arrival - POST/
locations/ {locationId}/ promotions/ validate - POST/
locations/ {locationId}/ gift-cards/ balance - GET/
customers/ me/ loyalty - POST/
customers/ token - GET/
customers/ me/ orders - GET/
customers/ me/ payment-methods - POSTorder.updated
Overview
One API to show a Real Time Ordering restaurant's menu, price a cart, place an order and follow it to the kitchen. It is built for self-order kiosks, branded apps and ordering partners.
Availability. Endpoints marked Live are running in production today. They accept either a partner API token or an RTO kiosk device token; a partner token reaches every location its account manages, and we issue one on request. Endpoints marked Planned are the agreed contract and may still change in detail.
Together the Live endpoints are enough to build a complete ordering client: read the menu, price a cart, place the order, and follow it to the kitchen. Card payment today is pay-at-counter or a card taken on RTO hardware; a card-not-present flow for your own app is planned.
Conventions
- Money is always an integer number of cents (
1299= $12.99). - Rates are decimal strings (
"0.0825"= 8.25%). - Timestamps are ISO-8601 with an offset (
2026-09-21T18:30:00-07:00); time zones are IANA names. - IDs are opaque strings.
locationIdis the restaurant's short name in its ordering URL. - Every order write needs an
Idempotency-Keyheader. Retrying with the same key returns the first result, so a retry can never place a second order. - The server sets the price. Show your own math if you like, but only the totals from
POST /orders/quoteandPOST /orderscount. A payment that does not equal the server total is refused before anything reaches the kitchen. - Errors share one envelope with a stable machine
code.
Placing an order
POST /orders/quote (validate and price) → take payment for exactly that total → POST /orders → follow GET /orders/{orderId} or the order.updated webhook. If the kitchen refuses an order after a card payment, the server voids the payment itself and says so in the response.
Endpoints at a glance
| Endpoint | What it does | Status |
|---|---|---|
GET /locations/{locationId} | Location profile, order types, fees, tipping and payment options | Live |
GET /locations/{locationId}/menu | Full orderable menu for a location and order type | Live |
GET /locations/{locationId}/menu/items/{itemId} | One item with its full modifier tree (for lazy-loading large menus) | Live |
GET /locations/{locationId}/schedule | Open hours and orderable time slots per order type | Live |
POST /locations/{locationId}/delivery/quote | Is this address deliverable, and at what fee, minimum and ETA? | Planned |
POST /orders/quote | Validate and price a cart exactly as POST /orders would, without placing it | Live |
POST /orders | Place an order | Live |
GET /orders/{orderId} | Order status and receipt | Live |
POST /orders/{orderId}/arrival | Curbside "I'm here" | Planned |
POST /locations/{locationId}/promotions/validate | Check a coupon code against a cart without placing an order | Planned |
POST /locations/{locationId}/gift-cards/balance | Gift card balance lookup | Planned |
GET /customers/me/loyalty | Points balance, punch cards and redeemable rewards at a location | Planned |
POST /customers/token | Exchange customer credentials (or an SMS one-time code) for a customer bearer token | Planned |
GET /customers/me/orders | Order history (reorder source) | Planned |
GET /customers/me/payment-methods | Saved cards (tokens only; brand + last4) | Planned |
POST order.updated | Sent to the client's registered URL whenever an order's status changes | Planned |
/locations/{locationId}LiveLocation profile, order types, fees, tipping and payment options
Parameters
| Parameter | In | Type | Notes |
|---|---|---|---|
locationId required | path | string | Restaurant orderUrl |
Responses
{
"locationId": "nancyjosburgers",
"name": "2pc Snack",
"phone": "(503) 555-0142",
"address": {
"line1": "2651 E Chapman Ave",
"line2": "line2",
"city": "Fullerton",
"state": "CA",
"postalCode": "92831",
"country": "US",
"lat": 33.87,
"lng": 33.87,
"instructions": "instructions"
},
"timeZone": "America/Los_Angeles",
"imageUrl": "imageUrl",
"logoUrl": "logoUrl",
"brandColor": "brandColor",
"acceptingOrders": true,
"openNow": true,
"taxRate": "0.0825",
"currency": "USD",
"orderTypes": [
{
"type": "pickup",
"enabled": true,
"asapAllowed": true,
"futureOrdersAllowed": true,
"maxDaysAhead": 1,
"prepTimeMinutes": 15,
"minimumOrder": 0,
"maximumOrder": 0,
"deliveryProvider": "merchant",
"vehicleInfoRequired": true,
"instructions": "instructions",
"paymentMethods": [
"card"
]
}
],
"tipping": {
"enabled": true,
"suggestions": [
{
"percent": "\u2026",
"isDefault": "\u2026"
}
]
},
"fees": [
{
"type": "service",
"label": "Service fee",
"flat": 1,
"percent": "percent",
"taxable": true
}
],
"guestCheckout": {
"allowed": true,
"emailRequired": true,
"phoneRequired": true
},
"cardTokenization": {
"provider": "shift4I4go",
"publicConfig": {}
},
"features": {
"specialInstructions": true,
"coupons": true,
"giftCards": true,
"loyalty": true,
"languages": [
"en"
]
}
}/locations/{locationId}/scheduleLiveOpen hours and orderable time slots per order type
Parameters
| Parameter | In | Type | Notes |
|---|---|---|---|
locationId required | path | string | Restaurant orderUrl |
orderType required | query | pickup | curbside | delivery | dineIn | |
days | query | integer |
Responses
{
"locationId": "nancyjosburgers",
"orderType": "pickup",
"timeZone": "America/Los_Angeles",
"now": "2026-09-21T18:30:00-07:00",
"asapAvailable": true,
"asapEstimate": "2026-09-21T18:30:00-07:00",
"nextOpen": "2026-09-21T18:30:00-07:00",
"days": [
{
"date": "2026-09-21",
"closed": true,
"closedReason": "closed",
"hours": [
{
"open": "\u2026",
"close": "\u2026"
}
],
"slots": [
{
"time": "\u2026",
"available": "\u2026"
}
]
}
]
}/locations/{locationId}/delivery/quotePlannedIs this address deliverable, and at what fee, minimum and ETA?
Stateless: checks an address without touching any cart or order.
Parameters
| Parameter | In | Type | Notes |
|---|---|---|---|
locationId required | path | string | Restaurant orderUrl |
Request body
{
"address": {
"line1": "2651 E Chapman Ave",
"line2": "line2",
"city": "Fullerton",
"state": "CA",
"postalCode": "92831",
"country": "US",
"lat": 33.87,
"lng": 33.87,
"instructions": "instructions"
},
"subtotal": 2340,
"requestedTime": "2026-09-21T18:30:00-07:00"
}
Responses
{
"deliverable": true,
"reason": "outsideZone",
"fee": 399,
"minimumOrder": 0,
"estimatedDeliveryTime": "2026-09-21T18:30:00-07:00",
"provider": "provider",
"zoneId": "zoneId"
}error.details.issues lists every problem./orders/quoteLiveValidate and price a cart exactly as POST /orders would, without placing it
Runs the full order pipeline in dry-run: item/modifier resolution, 86 check, hours/slot check, min order, delivery zone, discounts, tax, fees, tip and (for POS-priced stores) the POS price check. Returns every problem at once in issues[] (HTTP 200) so a client can fix the cart in one round trip. The returned quoteId may be passed to POST /orders within expiresAt; if the server total has changed by then, the order is rejected with PRICE_CHANGED and the new totals.
Request body
{
"locationId": "nancyjosburgers",
"lines": [
{
"lineId": "0",
"itemId": "282413",
"sizeId": "White",
"quantity": 2,
"modifiers": [
{
"groupId": "\u2026",
"modifierId": "\u2026"
}
],
"specialInstructions": "extra napkins",
"forName": "forName"
}
],
"orderType": "pickup",
"requestedTime": "2026-09-21T18:30:00-07:00",
"deliveryAddress": {
"line1": "2651 E Chapman Ave",
"line2": "line2",
"city": "Fullerton",
"state": "CA",
"postalCode": "92831",
"country": "US",
"lat": 33.87,
"lng": 33.87,
"instructions": "instructions"
},
"vehicle": {
"make": "make",
"model": "model",
"color": "color",
"plate": "plate"
},
"tableNumber": "tableNumber",
"tip": {
"amount": 2716,
"percent": 18
},
"promoCode": "promoCode",
"notes": "notes",
"customer": {
"firstName": "Sam",
"lastName": "Rivera",
"email": "[email protected]",
"phone": "(503) 555-0142",
"marketingOptIn": true
},
"payment": {
"method": "cardToken",
"cardToken": {
"token": "tok_example",
"postalCode": "92831"
},
"wallet": {
"type": "applePay",
"token": "tok_example"
},
"cardPresent": {
"paymentId": "paymentId"
},
"softPos": {
"recordNo": "recordNo",
"authCode": "04711A",
"refNo": "refNo",
"approvedAmount": "approvedAmount",
"cardType": "cardType",
"acctNoMasked": "acctNoMasked"
},
"savedCardId": "savedCardId",
"giftCards": [
{
"cardNumber": "\u2026",
"pin": "\u2026"
}
],
"loyaltyRewardIds": [
"loyaltyRewardIds"
]
},
"source": {
"channel": "kiosk",
"deviceId": "deviceId",
"partnerOrderId": "partnerOrderId"
}
}
Responses
issues — a quote can price and still be unorderable){
"quoteId": "q_7f3a",
"expiresAt": "2026-09-21T18:30:00-07:00",
"orderable": true,
"issues": [
{
"code": "ITEM_UNAVAILABLE",
"message": "2pc Snack is sold out.",
"lineId": "0",
"itemId": "282413",
"modifierGroupId": "modifierGroupId",
"nextAvailableTime": "2026-09-21T18:30:00-07:00"
}
],
"lines": [
{
"lineId": "0",
"itemId": "282413",
"name": "2pc Snack",
"sizeName": "sizeName",
"quantity": 2,
"unitPrice": 995,
"total": 2716,
"modifiers": [
{
"modifierId": "\u2026",
"name": "\u2026"
}
],
"specialInstructions": "extra napkins"
}
],
"totals": {
"subtotal": 2340,
"discount": 0,
"fees": [
{
"type": "\u2026",
"label": "\u2026"
}
],
"deliveryFee": 0,
"tax": 176,
"tip": 200,
"total": 2716,
"giftCardApplied": 0,
"amountDue": 2716
},
"estimatedReadyTime": "2026-09-21T18:30:00-07:00",
"upsells": [
{
"itemId": "282413",
"name": "2pc Snack",
"price": 995
}
]
}/ordersLivePlace an order
Re-validates and re-prices server-side. Payment is then taken/verified for exactly the server total, the order is persisted, and it is sent to the POS.
Payment is verified, never trusted. cardPresent must reference a /payments/v1 record that is approved, belongs to this location, is unused, and whose amount equals the order total. cardToken / wallet are charged by the server. A client can never mark an order paid by itself.
If POS submission fails after payment, the server voids/refunds and returns 502 POS_REJECTED with payment.status — the client does not need to void.
Parameters
| Parameter | In | Type | Notes |
|---|---|---|---|
Idempotency-Key required | header | string · uuid |
Request body
{
"locationId": "nancyjosburgers",
"lines": [
{
"lineId": "0",
"itemId": "282413",
"sizeId": "White",
"quantity": 2,
"modifiers": [
{}
],
"specialInstructions": "extra napkins",
"forName": "forName"
}
],
"orderType": "pickup",
"requestedTime": "2026-09-21T18:30:00-07:00",
"deliveryAddress": {
"line1": "2651 E Chapman Ave",
"line2": "line2",
"city": "Fullerton",
"state": "CA",
"postalCode": "92831",
"country": "US",
"lat": 33.87,
"lng": 33.87,
"instructions": "instructions"
},
"vehicle": {
"make": "make",
"model": "model",
"color": "color",
"plate": "plate"
},
"tableNumber": "tableNumber",
"tip": {
"amount": 2716,
"percent": 18
},
"promoCode": "promoCode",
"notes": "notes",
"customer": {
"firstName": "Sam",
"lastName": "Rivera",
"email": "[email protected]",
"phone": "(503) 555-0142",
"marketingOptIn": true
},
"payment": {
"method": "cardToken",
"cardToken": {
"token": "tok_example",
"postalCode": "92831"
},
"wallet": {
"type": "applePay",
"token": "tok_example"
},
"cardPresent": {
"paymentId": "paymentId"
},
"softPos": {
"recordNo": "recordNo",
"authCode": "04711A",
"refNo": "refNo",
"approvedAmount": "approvedAmount",
"cardType": "cardType",
"acctNoMasked": "acctNoMasked"
},
"savedCardId": "savedCardId",
"giftCards": [
{
"cardNumber": "\u2026",
"pin": "\u2026"
}
],
"loyaltyRewardIds": [
"loyaltyRewardIds"
]
},
"source": {
"channel": "kiosk",
"deviceId": "deviceId",
"partnerOrderId": "partnerOrderId"
},
"quoteId": "q_7f3a",
"expectedTotal": 2716
}
Responses
{
"orderId": "21707901",
"orderNumber": "1093",
"accessToken": "accessToken",
"locationId": "nancyjosburgers",
"status": "received",
"statusReason": "statusReason",
"orderType": "pickup",
"placedAt": "2026-09-21T18:30:00-07:00",
"requestedTime": "2026-09-21T18:30:00-07:00",
"estimatedReadyTime": "2026-09-21T18:30:00-07:00",
"lines": [
{
"lineId": "0",
"itemId": "282413",
"name": "2pc Snack",
"sizeName": "sizeName",
"quantity": 2,
"unitPrice": 995,
"total": 2716,
"modifiers": [
{
"modifierId": "\u2026",
"name": "\u2026"
}
],
"specialInstructions": "extra napkins"
}
],
"removedLines": [
"removedLines"
],
"totals": {
"subtotal": 2340,
"discount": 0,
"fees": [
{
"type": "\u2026",
"label": "\u2026"
}
],
"deliveryFee": 0,
"tax": 176,
"tip": 200,
"total": 2716,
"giftCardApplied": 0,
"amountDue": 2716
},
"payment": {
"method": "method",
"status": "pending",
"amount": 2716,
"brand": "VISA",
"last4": "1111",
"authCode": "04711A",
"reference": "58"
},
"customer": {
"firstName": "Sam",
"lastName": "Rivera",
"email": "[email protected]",
"phone": "(503) 555-0142",
"marketingOptIn": true
},
"deliveryAddress": {
"line1": "2651 E Chapman Ave",
"line2": "line2",
"city": "Fullerton",
"state": "CA",
"postalCode": "92831",
"country": "US",
"lat": 33.87,
"lng": 33.87,
"instructions": "instructions"
},
"delivery": {
"provider": "provider",
"trackingUrl": "trackingUrl",
"driverName": "driverName"
},
"pos": {
"system": "skytab",
"checkNumber": "checkNumber",
"status": "pending"
},
"trackingUrl": "trackingUrl"
}{
"orderId": "21707901",
"orderNumber": "1093",
"accessToken": "accessToken",
"locationId": "nancyjosburgers",
"status": "received",
"statusReason": "statusReason",
"orderType": "pickup",
"placedAt": "2026-09-21T18:30:00-07:00",
"requestedTime": "2026-09-21T18:30:00-07:00",
"estimatedReadyTime": "2026-09-21T18:30:00-07:00",
"lines": [
{
"lineId": "0",
"itemId": "282413",
"name": "2pc Snack",
"sizeName": "sizeName",
"quantity": 2,
"unitPrice": 995,
"total": 2716,
"modifiers": [
{
"modifierId": "\u2026",
"name": "\u2026"
}
],
"specialInstructions": "extra napkins"
}
],
"removedLines": [
"removedLines"
],
"totals": {
"subtotal": 2340,
"discount": 0,
"fees": [
{
"type": "\u2026",
"label": "\u2026"
}
],
"deliveryFee": 0,
"tax": 176,
"tip": 200,
"total": 2716,
"giftCardApplied": 0,
"amountDue": 2716
},
"payment": {
"method": "method",
"status": "pending",
"amount": 2716,
"brand": "VISA",
"last4": "1111",
"authCode": "04711A",
"reference": "58"
},
"customer": {
"firstName": "Sam",
"lastName": "Rivera",
"email": "[email protected]",
"phone": "(503) 555-0142",
"marketingOptIn": true
},
"deliveryAddress": {
"line1": "2651 E Chapman Ave",
"line2": "line2",
"city": "Fullerton",
"state": "CA",
"postalCode": "92831",
"country": "US",
"lat": 33.87,
"lng": 33.87,
"instructions": "instructions"
},
"delivery": {
"provider": "provider",
"trackingUrl": "trackingUrl",
"driverName": "driverName"
},
"pos": {
"system": "skytab",
"checkNumber": "checkNumber",
"status": "pending"
},
"trackingUrl": "trackingUrl"
}error.details.issues lists every problem./orders/{orderId}LiveOrder status and receipt
Callable with the API key that created the order, or with the order's accessToken (returned at creation; safe to put in a customer-facing tracking link). Order ids are not sequential and are not the POS/restaurant order number.
Auth: apiKey, orderToken
Parameters
| Parameter | In | Type | Notes |
|---|---|---|---|
orderId required | path | string |
Responses
{
"orderId": "21707901",
"orderNumber": "1093",
"accessToken": "accessToken",
"locationId": "nancyjosburgers",
"status": "received",
"statusReason": "statusReason",
"orderType": "pickup",
"placedAt": "2026-09-21T18:30:00-07:00",
"requestedTime": "2026-09-21T18:30:00-07:00",
"estimatedReadyTime": "2026-09-21T18:30:00-07:00",
"lines": [
{
"lineId": "0",
"itemId": "282413",
"name": "2pc Snack",
"sizeName": "sizeName",
"quantity": 2,
"unitPrice": 995,
"total": 2716,
"modifiers": [
{
"modifierId": "\u2026",
"name": "\u2026"
}
],
"specialInstructions": "extra napkins"
}
],
"removedLines": [
"removedLines"
],
"totals": {
"subtotal": 2340,
"discount": 0,
"fees": [
{
"type": "\u2026",
"label": "\u2026"
}
],
"deliveryFee": 0,
"tax": 176,
"tip": 200,
"total": 2716,
"giftCardApplied": 0,
"amountDue": 2716
},
"payment": {
"method": "method",
"status": "pending",
"amount": 2716,
"brand": "VISA",
"last4": "1111",
"authCode": "04711A",
"reference": "58"
},
"customer": {
"firstName": "Sam",
"lastName": "Rivera",
"email": "[email protected]",
"phone": "(503) 555-0142",
"marketingOptIn": true
},
"deliveryAddress": {
"line1": "2651 E Chapman Ave",
"line2": "line2",
"city": "Fullerton",
"state": "CA",
"postalCode": "92831",
"country": "US",
"lat": 33.87,
"lng": 33.87,
"instructions": "instructions"
},
"delivery": {
"provider": "provider",
"trackingUrl": "trackingUrl",
"driverName": "driverName"
},
"pos": {
"system": "skytab",
"checkNumber": "checkNumber",
"status": "pending"
},
"trackingUrl": "trackingUrl"
}/orders/{orderId}/arrivalPlannedCurbside "I'm here"
Auth: apiKey, orderToken
Parameters
| Parameter | In | Type | Notes |
|---|---|---|---|
orderId required | path | string |
Request body
{
"vehicle": {
"make": "make",
"model": "model",
"color": "color",
"plate": "plate"
},
"parkingSpot": "parkingSpot"
}
Responses
/locations/{locationId}/promotions/validatePlannedCheck a coupon code against a cart without placing an order
Parameters
| Parameter | In | Type | Notes |
|---|---|---|---|
locationId required | path | string | Restaurant orderUrl |
Request body
{
"code": "ITEM_UNAVAILABLE",
"cart": {
"locationId": "nancyjosburgers",
"lines": [
{
"lineId": "\u2026",
"itemId": "\u2026"
}
]
},
"email": "[email protected]"
}
Responses
{
"valid": true,
"discount": 0,
"description": "description",
"reason": "reason"
}/locations/{locationId}/gift-cards/balancePlannedGift card balance lookup
Parameters
| Parameter | In | Type | Notes |
|---|---|---|---|
locationId required | path | string | Restaurant orderUrl |
Request body
{
"cardNumber": "cardNumber",
"pin": "pin"
}
Responses
{
"balance": 2500,
"currency": "USD"
}error.details.issues lists every problem./customers/me/loyaltyPlannedPoints balance, punch cards and redeemable rewards at a location
Auth: customerToken
Parameters
| Parameter | In | Type | Notes |
|---|---|---|---|
locationId required | query | string |
Responses
{
"enrolled": true,
"points": 1,
"pointsValue": 1,
"punchCards": [
{
"id": "id",
"name": "2pc Snack",
"punches": 1,
"threshold": 1
}
],
"rewards": [
{
"id": "id",
"name": "2pc Snack",
"pointsCost": 1,
"value": 1,
"redeemable": true
}
]
}/customers/tokenPlannedExchange customer credentials (or an SMS one-time code) for a customer bearer token
Scoped to one merchant. Replaces the hash/username cookie login for API clients.
Request body
{
"locationId": "nancyjosburgers",
"email": "[email protected]",
"password": "password"
}
Responses
{
"accessToken": "accessToken",
"expiresAt": "2026-09-21T18:30:00-07:00",
"customer": {
"firstName": "Sam",
"lastName": "Rivera",
"email": "[email protected]",
"phone": "(503) 555-0142",
"marketingOptIn": true
}
}/customers/me/ordersPlannedOrder history (reorder source)
Auth: customerToken
Parameters
| Parameter | In | Type | Notes |
|---|---|---|---|
locationId | query | string |
Responses
{
"orders": [
{
"orderId": "21707901",
"orderNumber": "1093",
"accessToken": "accessToken",
"locationId": "nancyjosburgers",
"status": "received",
"statusReason": "statusReason",
"orderType": "pickup",
"placedAt": "2026-09-21T18:30:00-07:00",
"requestedTime": "2026-09-21T18:30:00-07:00",
"estimatedReadyTime": "2026-09-21T18:30:00-07:00",
"lines": [
{
"lineId": "\u2026",
"itemId": "\u2026"
}
],
"removedLines": [
"removedLines"
],
"totals": {
"subtotal": "\u2026",
"discount": "\u2026"
},
"payment": {
"method": "\u2026",
"status": "\u2026"
},
"customer": {
"firstName": "\u2026",
"lastName": "\u2026"
},
"deliveryAddress": {
"line1": "\u2026",
"line2": "\u2026"
},
"delivery": {
"provider": "\u2026",
"trackingUrl": "\u2026"
},
"pos": {
"system": "\u2026",
"checkNumber": "\u2026"
},
"trackingUrl": "trackingUrl"
}
]
}/customers/me/payment-methodsPlannedSaved cards (tokens only; brand + last4)
Auth: customerToken
Responses
[
{
"id": "id",
"brand": "VISA",
"last4": "1111",
"expMonth": 1,
"expYear": 1
}
]order.updatedPlannedSent to the client's registered URL whenever an order's status changes
Signed like our Shift4 loyalty inbound: header X-RTO-Signature: t=<unix>,v1=<hex> where v1 = HMAC-SHA256(secret, t + "." + rawBody). Reject if |now - t| > 300s. Retries with exponential backoff for 24h; eventId is stable across retries.
Request body
{
"eventId": "eventId",
"type": "order.updated",
"createdAt": "2026-09-21T18:30:00-07:00",
"order": {
"orderId": "21707901",
"orderNumber": "1093",
"accessToken": "accessToken",
"locationId": "nancyjosburgers",
"status": "received",
"statusReason": "statusReason",
"orderType": "pickup",
"placedAt": "2026-09-21T18:30:00-07:00",
"requestedTime": "2026-09-21T18:30:00-07:00",
"estimatedReadyTime": "2026-09-21T18:30:00-07:00",
"lines": [
{
"lineId": "\u2026",
"itemId": "\u2026"
}
],
"removedLines": [
"removedLines"
],
"totals": {
"subtotal": 2340,
"discount": 0,
"fees": [
{
"type": "\u2026",
"label": "\u2026"
}
],
"deliveryFee": 0,
"tax": 176,
"tip": 200,
"total": 2716,
"giftCardApplied": 0,
"amountDue": 2716
},
"payment": {
"method": "method",
"status": "pending",
"amount": 2716,
"brand": "VISA",
"last4": "1111",
"authCode": "04711A",
"reference": "58"
},
"customer": {
"firstName": "Sam",
"lastName": "Rivera",
"email": "[email protected]",
"phone": "(503) 555-0142",
"marketingOptIn": true
},
"deliveryAddress": {
"line1": "2651 E Chapman Ave",
"line2": "line2",
"city": "Fullerton",
"state": "CA",
"postalCode": "92831",
"country": "US",
"lat": 33.87,
"lng": 33.87,
"instructions": "instructions"
},
"delivery": {
"provider": "provider",
"trackingUrl": "trackingUrl",
"driverName": "driverName"
},
"pos": {
"system": "skytab",
"checkNumber": "checkNumber",
"status": "pending"
},
"trackingUrl": "trackingUrl"
}
}
Responses
Errors
Every error uses the same envelope: {"error": {"code", "message", "details"}}. The message is safe to show a customer; switch on code. When a cart can't be placed, details.issues lists every problem at once, and after a refused card payment details.payment says whether it was voided.
UNAUTHORIZEDFORBIDDENNOT_FOUNDVALIDATION_FAILEDLOCATION_UNAVAILABLELOCATION_CLOSEDORDER_TYPE_UNAVAILABLESLOT_UNAVAILABLEITEM_UNAVAILABLEITEM_NOT_ON_MENUINVALID_MODIFIERSBELOW_MINIMUMABOVE_MAXIMUMOUTSIDE_DELIVERY_ZONEPROMOTION_INVALIDPRICE_CHANGEDIDEMPOTENCY_CONFLICTPAYMENT_DECLINEDPAYMENT_AMOUNT_MISMATCHPAYMENT_NOT_APPROVEDPAYMENT_ALREADY_USEDPAYMENT_METHOD_NOT_ALLOWEDPOS_REJECTEDRATE_LIMITEDINTERNALOpenAPI file
The full OpenAPI 3.1 description of everything on this page, for code generators and API tools. Download openapi.yaml or copy it below.
Show OpenAPI 3.1 YAML
openapi: 3.1.0
info:
title: RTO Ordering API
version: 1.0.0
summary: Menu, pricing, ordering and order status for Real Time Ordering (GimmeGrub) locations.
description: "One API to show a Real Time Ordering restaurant's menu, price a cart, place an order and\n\
follow it to the kitchen. It is built for self-order kiosks, branded apps and ordering partners.\n\
\n**Availability.** Endpoints marked *Live* are running in production today. They accept either a\n\
partner API token or an RTO kiosk device token; a partner token reaches every location its account\n\
manages, and we issue one on request. Endpoints marked *Planned* are the agreed contract and may\n\
still change in detail.\n\nTogether the Live endpoints are enough to build a complete ordering client:\
\ read the menu, price a\ncart, place the order, and follow it to the kitchen. Card payment today\
\ is pay-at-counter or a card\ntaken on RTO hardware; a card-not-present flow for your own app is\
\ planned.\n\n## Conventions\n- **Money** is always an integer number of **cents** (`1299` = $12.99).\n\
- **Rates** are decimal strings (`\"0.0825\"` = 8.25%).\n- **Timestamps** are ISO-8601 with an offset\
\ (`2026-09-21T18:30:00-07:00`); time zones are IANA names.\n- **IDs** are opaque strings. `locationId`\
\ is the restaurant's short name in its ordering URL.\n- **Every order write** needs an `Idempotency-Key`\
\ header. Retrying with the same key returns the first\n result, so a retry can never place a second\
\ order.\n- **The server sets the price.** Show your own math if you like, but only the totals from\n\
\ `POST /orders/quote` and `POST /orders` count. A payment that does not equal the server total is\
\ refused\n before anything reaches the kitchen.\n- **Errors** share one envelope with a stable machine\
\ `code`.\n\n## Placing an order\n`POST /orders/quote` (validate and price) → take payment for exactly\
\ that total → `POST /orders` →\nfollow `GET /orders/{orderId}` or the `order.updated` webhook. If\
\ the kitchen refuses an order after a card\npayment, the server voids the payment itself and says\
\ so in the response.\n"
contact:
name: Real Time Ordering
url: https://www.realtimeordering.com
servers:
- url: https://www.gimmegrub.com/api/v1
description: Production
security:
- apiKey: []
tags:
- name: Locations
- name: Menu
- name: Scheduling
- name: Delivery
- name: Orders
- name: Promotions
- name: GiftCards
- name: Loyalty
- name: Customers
- name: Devices
- name: Webhooks
paths:
/locations/{locationId}:
get:
tags:
- Locations
operationId: getLocation
summary: Location profile, order types, fees, tipping and payment options
parameters:
- $ref: '#/components/parameters/locationId'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Location'
'404':
$ref: '#/components/responses/NotFound'
x-status: live
/locations/{locationId}/menu:
get:
tags:
- Menu
operationId: getMenu
summary: Full orderable menu for a location and order type
description: 'Returns the active menu with sizes and nested modifier groups. Unavailable (86''d)
items
are **included** with `available: false` so clients can show "sold out" and reject stale
carts deterministically. Supports `ETag` / `If-None-Match`.
'
parameters:
- $ref: '#/components/parameters/locationId'
- name: orderType
in: query
schema:
$ref: '#/components/schemas/OrderType'
- name: at
in: query
description: Evaluate menu/category availability at this time (future orders). Defaults to now.
schema:
type: string
format: date-time
- name: If-None-Match
in: header
schema:
type: string
responses:
'200':
description: OK
headers:
ETag:
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/Menu'
'304':
description: Not modified
'404':
$ref: '#/components/responses/NotFound'
x-status: live
/locations/{locationId}/menu/items/{itemId}:
get:
tags:
- Menu
operationId: getMenuItem
summary: One item with its full modifier tree (for lazy-loading large menus)
parameters:
- $ref: '#/components/parameters/locationId'
- name: itemId
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/MenuItem'
'404':
$ref: '#/components/responses/NotFound'
x-status: live
/locations/{locationId}/schedule:
get:
tags:
- Scheduling
operationId: getSchedule
summary: Open hours and orderable time slots per order type
parameters:
- $ref: '#/components/parameters/locationId'
- name: orderType
in: query
required: true
schema:
$ref: '#/components/schemas/OrderType'
- name: days
in: query
schema:
type: integer
minimum: 1
maximum: 31
default: 7
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Schedule'
x-status: live
/locations/{locationId}/delivery/quote:
post:
tags:
- Delivery
operationId: quoteDelivery
summary: Is this address deliverable, and at what fee, minimum and ETA?
description: 'Stateless: checks an address without touching any cart or order.'
parameters:
- $ref: '#/components/parameters/locationId'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- address
properties:
address:
$ref: '#/components/schemas/Address'
subtotal:
type: integer
description: cents; affects min-order and %-based fees
requestedTime:
type: string
format: date-time
description: omit for ASAP
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/DeliveryQuote'
'422':
$ref: '#/components/responses/Unprocessable'
x-status: planned
/locations/{locationId}/promotions/validate:
post:
tags:
- Promotions
operationId: validatePromotion
summary: Check a coupon code against a cart without placing an order
parameters:
- $ref: '#/components/parameters/locationId'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- code
- cart
properties:
code:
type: string
cart:
$ref: '#/components/schemas/Cart'
email:
type: string
description: for single-use-per-customer coupons
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
valid:
type: boolean
discount:
type: integer
description: cents
description:
type: string
reason:
type: string
description: present when valid=false (EXPIRED, MIN_NOT_MET, ALREADY_USED, NOT_FOUND)
x-status: planned
/locations/{locationId}/gift-cards/balance:
post:
tags:
- GiftCards
operationId: giftCardBalance
summary: Gift card balance lookup
parameters:
- $ref: '#/components/parameters/locationId'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- cardNumber
properties:
cardNumber:
type: string
pin:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
balance:
type: integer
description: cents
currency:
type: string
example: USD
'422':
$ref: '#/components/responses/Unprocessable'
'429':
$ref: '#/components/responses/RateLimited'
x-status: planned
/orders/quote:
post:
tags:
- Orders
operationId: quoteOrder
summary: Validate and price a cart exactly as POST /orders would, without placing it
description: 'Runs the full order pipeline in dry-run: item/modifier resolution, 86 check, hours/slot
check, min order, delivery zone, discounts, tax, fees, tip and (for POS-priced stores) the
POS price check. Returns every problem at once in `issues[]` (HTTP 200) so a client can
fix the cart in one round trip. The returned `quoteId` may be passed to `POST /orders`
within `expiresAt`; if the server total has changed by then, the order is rejected with
`PRICE_CHANGED` and the new totals.
'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/OrderRequest'
responses:
'200':
description: Priced (check `issues` — a quote can price and still be unorderable)
content:
application/json:
schema:
$ref: '#/components/schemas/Quote'
'404':
$ref: '#/components/responses/NotFound'
x-status: live
/orders:
post:
tags:
- Orders
operationId: createOrder
summary: Place an order
description: 'Re-validates and re-prices server-side. Payment is then taken/verified for exactly
the
server total, the order is persisted, and it is sent to the POS.
**Payment is verified, never trusted.** `cardPresent` must reference a `/payments/v1`
record that is `approved`, belongs to this location, is unused, and whose amount equals
the order total. `cardToken` / `wallet` are charged by the server. A client can never
mark an order paid by itself.
If POS submission fails after payment, the server voids/refunds and returns
`502 POS_REJECTED` with `payment.status` — the client does not need to void.
'
parameters:
- $ref: '#/components/parameters/idempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/OrderRequest'
- type: object
required:
- customer
- payment
properties:
quoteId:
type: string
expectedTotal:
type: integer
description: Cents. If given and ≠ server total, reject with PRICE_CHANGED before
charging.
responses:
'201':
description: Order accepted
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'200':
description: Idempotent replay of an order already created with this key
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'402':
$ref: '#/components/responses/PaymentFailed'
'409':
$ref: '#/components/responses/Conflict'
'422':
$ref: '#/components/responses/Unprocessable'
'502':
$ref: '#/components/responses/PosRejected'
x-status: live
/orders/{orderId}:
get:
tags:
- Orders
operationId: getOrder
summary: Order status and receipt
description: 'Callable with the API key that created the order, or with the order''s `accessToken`
(returned at creation; safe to put in a customer-facing tracking link). Order ids are
not sequential and are not the POS/restaurant order number.
'
security:
- apiKey: []
- orderToken: []
parameters:
- $ref: '#/components/parameters/orderId'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'404':
$ref: '#/components/responses/NotFound'
x-status: live
/orders/{orderId}/arrival:
post:
tags:
- Orders
operationId: notifyArrival
summary: Curbside "I'm here"
security:
- apiKey: []
- orderToken: []
parameters:
- $ref: '#/components/parameters/orderId'
requestBody:
content:
application/json:
schema:
type: object
properties:
vehicle:
$ref: '#/components/schemas/Vehicle'
parkingSpot:
type: string
responses:
'204':
description: Merchant notified
x-status: planned
/customers/token:
post:
tags:
- Customers
operationId: customerLogin
summary: Exchange customer credentials (or an SMS one-time code) for a customer bearer token
description: Scoped to one merchant. Replaces the `hash`/`username` cookie login for API clients.
requestBody:
required: true
content:
application/json:
schema:
oneOf:
- type: object
required:
- locationId
- email
- password
properties:
locationId:
type: string
email:
type: string
password:
type: string
- type: object
required:
- locationId
- phone
- otp
properties:
locationId:
type: string
phone:
type: string
otp:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
accessToken:
type: string
expiresAt:
type: string
format: date-time
customer:
$ref: '#/components/schemas/Customer'
'401':
$ref: '#/components/responses/Unauthorized'
x-status: planned
/customers/me/orders:
get:
tags:
- Customers
operationId: myOrders
summary: Order history (reorder source)
security:
- customerToken: []
parameters:
- name: locationId
in: query
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
orders:
type: array
items:
$ref: '#/components/schemas/Order'
x-status: planned
/customers/me/payment-methods:
get:
tags:
- Customers
operationId: myPaymentMethods
summary: Saved cards (tokens only; brand + last4)
security:
- customerToken: []
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: string
brand:
type: string
last4:
type: string
expMonth:
type: integer
expYear:
type: integer
description: 4-digit
x-status: planned
/customers/me/loyalty:
get:
tags:
- Loyalty
operationId: myLoyalty
summary: Points balance, punch cards and redeemable rewards at a location
security:
- customerToken: []
parameters:
- name: locationId
in: query
required: true
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/LoyaltyAccount'
x-status: planned
webhooks:
order.updated:
post:
tags:
- Webhooks
summary: Sent to the client's registered URL whenever an order's status changes
description: 'Signed like our Shift4 loyalty inbound: header `X-RTO-Signature: t=<unix>,v1=<hex>`
where
v1 = HMAC-SHA256(secret, t + "." + rawBody). Reject if |now - t| > 300s. Retries with
exponential backoff for 24h; `eventId` is stable across retries.
'
requestBody:
content:
application/json:
schema:
type: object
properties:
eventId:
type: string
type:
type: string
const: order.updated
createdAt:
type: string
format: date-time
order:
$ref: '#/components/schemas/Order'
responses:
2XX:
description: Acknowledged
components:
securitySchemes:
apiKey:
type: http
scheme: bearer
description: '`Authorization: Bearer <key>`. Keys are issued per client (kiosk fleet, app or partner)
and scoped to named locations. RTO kiosks use their enrolled device token here, limited to their
own location.'
orderToken:
type: apiKey
in: query
name: token
description: Per-order read token returned as `Order.accessToken`.
customerToken:
type: http
scheme: bearer
description: From POST /customers/token. Must be sent together with the client's apiKey via `X-RTO-Api-Key`.
parameters:
locationId:
name: locationId
in: path
required: true
description: Restaurant orderUrl
schema:
type: string
orderId:
name: orderId
in: path
required: true
schema:
type: string
idempotencyKey:
name: Idempotency-Key
in: header
required: true
schema:
type: string
format: uuid
responses:
NotFound:
description: Unknown location, item or order
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Missing/invalid credentials
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unprocessable:
description: Cart/order cannot be placed as sent. `error.details.issues` lists every problem.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Conflict:
description: PRICE_CHANGED (details.quote has new totals), IDEMPOTENCY_CONFLICT, or PAYMENT_ALREADY_USED
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
PaymentFailed:
description: PAYMENT_DECLINED / PAYMENT_AMOUNT_MISMATCH / PAYMENT_NOT_APPROVED. Nothing sent to
the POS.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
PosRejected:
description: POS refused the order; payment already voided/refunded (see details.payment).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
RateLimited:
description: Too many requests; honour Retry-After
headers:
Retry-After:
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
schemas:
Error:
type: object
required:
- error
properties:
error:
type: object
required:
- code
- message
properties:
code:
type: string
enum:
- UNAUTHORIZED
- FORBIDDEN
- NOT_FOUND
- VALIDATION_FAILED
- LOCATION_UNAVAILABLE
- LOCATION_CLOSED
- ORDER_TYPE_UNAVAILABLE
- SLOT_UNAVAILABLE
- ITEM_UNAVAILABLE
- ITEM_NOT_ON_MENU
- INVALID_MODIFIERS
- BELOW_MINIMUM
- ABOVE_MAXIMUM
- OUTSIDE_DELIVERY_ZONE
- PROMOTION_INVALID
- PRICE_CHANGED
- IDEMPOTENCY_CONFLICT
- PAYMENT_DECLINED
- PAYMENT_AMOUNT_MISMATCH
- PAYMENT_NOT_APPROVED
- PAYMENT_ALREADY_USED
- PAYMENT_METHOD_NOT_ALLOWED
- POS_REJECTED
- RATE_LIMITED
- INTERNAL
message:
type: string
description: Human readable; safe to show a customer
details:
type: object
properties:
issues:
type: array
items:
$ref: '#/components/schemas/Issue'
quote:
$ref: '#/components/schemas/Quote'
payment:
$ref: '#/components/schemas/PaymentResult'
requestId:
type: string
Issue:
type: object
required:
- code
- message
properties:
code:
type: string
description: One of Error.code values
message:
type: string
lineId:
type: string
description: Cart line the issue refers to
itemId:
type: string
modifierGroupId:
type: string
nextAvailableTime:
type: string
format: date-time
OrderType:
type: string
enum:
- pickup
- curbside
- delivery
- dineIn
description: 'pickup = carry-out; curbside = pickup + vehicle; delivery = own fleet or DaaS (see
Location.orderTypes[].deliveryProvider); dineIn = table service / kiosk eat-in.
Catering is a pickup/delivery order with a future `requestedTime` (Location.catering rules).
'
Location:
type: object
properties:
locationId:
type: string
name:
type: string
phone:
type: string
address:
$ref: '#/components/schemas/Address'
timeZone:
type: string
example: America/Los_Angeles
imageUrl:
type: string
logoUrl:
type: string
brandColor:
type: string
acceptingOrders:
type: boolean
description: false when paused/unavailable (admin pause-for-today)
openNow:
type: boolean
taxRate:
type: string
example: '0.0825'
currency:
type: string
example: USD
orderTypes:
type: array
items:
type: object
properties:
type:
$ref: '#/components/schemas/OrderType'
enabled:
type: boolean
asapAllowed:
type: boolean
futureOrdersAllowed:
type: boolean
maxDaysAhead:
type: integer
prepTimeMinutes:
type: integer
minimumOrder:
type: integer
description: cents
maximumOrder:
type: integer
description: cents; 0 = none
deliveryProvider:
type: string
enum:
- merchant
- doordash
- uber
- firstDelivery
description: delivery only
vehicleInfoRequired:
type: boolean
instructions:
type: string
paymentMethods:
type: array
items:
type: string
enum:
- card
- applePay
- googlePay
- giftCard
- payAtStore
tipping:
type: object
properties:
enabled:
type: boolean
suggestions:
type: array
items:
type: object
properties:
percent:
type: integer
isDefault:
type: boolean
fees:
type: array
description: Service / convenience / CC fees that may appear on the quote
items:
type: object
properties:
type:
type: string
enum:
- service
- convenience
- creditCard
- bag
- deposit
label:
type: string
flat:
type: integer
description: cents
percent:
type: string
taxable:
type: boolean
guestCheckout:
type: object
properties:
allowed:
type: boolean
emailRequired:
type: boolean
phoneRequired:
type: boolean
cardTokenization:
type: object
description: How a client obtains a `cardToken` for this location
properties:
provider:
type: string
enum:
- shift4I4go
- stripe
- datacap
publicConfig:
type: object
additionalProperties: true
features:
type: object
properties:
specialInstructions:
type: boolean
coupons:
type: boolean
giftCards:
type: boolean
loyalty:
type: boolean
languages:
type: array
items:
type: string
example:
- en
- es
Menu:
type: object
properties:
menuId:
type: string
name:
type: string
currency:
type: string
updatedAt:
type: string
format: date-time
categories:
type: array
items:
$ref: '#/components/schemas/MenuCategory'
MenuCategory:
type: object
properties:
id:
type: string
name:
type: string
description:
type: string
imageUrl:
type: string
sortOrder:
type: integer
availability:
$ref: '#/components/schemas/Availability'
items:
type: array
items:
$ref: '#/components/schemas/MenuItem'
MenuItem:
type: object
properties:
id:
type: string
name:
type: string
description:
type: string
imageUrl:
type: string
price:
type: integer
description: cents; base price when there are no sizes
sizes:
type: array
description: Mutually exclusive priced variants (exactly one must be chosen when present)
items:
type: object
properties:
id:
type: string
name:
type: string
price:
type: integer
available:
type: boolean
isDefault:
type: boolean
modifierGroups:
type: array
items:
$ref: '#/components/schemas/ModifierGroup'
available:
type: boolean
description: false = 86'd / sold out
availability:
$ref: '#/components/schemas/Availability'
taxRate:
type: string
description: overrides location taxRate when present
tags:
type: array
items:
type: string
example:
- alcohol
- spicy
- vegetarian
minimumAge:
type: integer
maxQuantity:
type: integer
specialInstructionsAllowed:
type: boolean
posId:
type: string
description: POS PLU / item GUID, informational
ModifierGroup:
type: object
properties:
id:
type: string
name:
type: string
minSelections:
type: integer
maxSelections:
type: integer
description: 0 = unlimited
maxPerModifier:
type: integer
description: max quantity of a single modifier (e.g. extra cheese x2)
style:
type: string
enum:
- standard
- pizzaTopping
description: pizzaTopping allows placement left/right/whole
modifiers:
type: array
items:
$ref: '#/components/schemas/Modifier'
Modifier:
type: object
properties:
id:
type: string
name:
type: string
price:
type: integer
description: cents
sizePrices:
type: object
additionalProperties:
type: integer
description: sizeId → price when price depends on the chosen size
halfPrice:
type: integer
description: cents when placed on one half (pizzaTopping)
isDefault:
type: boolean
description: pre-selected / included
available:
type: boolean
modifierGroups:
type: array
description: nested groups (e.g. "choose dressing" under a side)
items:
$ref: '#/components/schemas/ModifierGroup'
Availability:
type: object
description: When absent, available whenever the location is open.
properties:
windows:
type: array
items:
type: object
properties:
days:
type: array
items:
type: string
enum:
- mon
- tue
- wed
- thu
- fri
- sat
- sun
start:
type: string
example: '11:00'
end:
type: string
example: '15:00'
orderTypes:
type: array
items:
$ref: '#/components/schemas/OrderType'
Schedule:
type: object
properties:
locationId:
type: string
orderType:
$ref: '#/components/schemas/OrderType'
timeZone:
type: string
now:
type: string
format: date-time
asapAvailable:
type: boolean
asapEstimate:
type: string
format: date-time
nextOpen:
type: string
format: date-time
days:
type: array
items:
type: object
properties:
date:
type: string
format: date
closed:
type: boolean
closedReason:
type: string
enum:
- closed
- holiday
- paused
hours:
type: array
items:
type: object
properties:
open:
type: string
format: date-time
close:
type: string
format: date-time
slots:
type: array
items:
type: object
properties:
time:
type: string
format: date-time
available:
type: boolean
reason:
type: string
enum:
- full
- pastCutoff
- leadTime
Address:
type: object
properties:
line1:
type: string
line2:
type: string
city:
type: string
state:
type: string
postalCode:
type: string
country:
type: string
default: US
lat:
type: number
lng:
type: number
instructions:
type: string
description: gate code, apartment notes
Vehicle:
type: object
properties:
make:
type: string
model:
type: string
color:
type: string
plate:
type: string
DeliveryQuote:
type: object
properties:
deliverable:
type: boolean
reason:
type: string
enum:
- outsideZone
- belowMinimum
- providerUnavailable
- closed
fee:
type: integer
description: cents
minimumOrder:
type: integer
estimatedDeliveryTime:
type: string
format: date-time
provider:
type: string
zoneId:
type: string
Cart:
type: object
required:
- locationId
- lines
properties:
locationId:
type: string
lines:
type: array
minItems: 1
items:
$ref: '#/components/schemas/CartLine'
CartLine:
type: object
required:
- itemId
- quantity
properties:
lineId:
type: string
description: client-chosen; echoed in issues and receipt
itemId:
type: string
sizeId:
type: string
quantity:
type: integer
minimum: 1
modifiers:
type: array
items:
$ref: '#/components/schemas/CartModifier'
specialInstructions:
type: string
maxLength: 140
forName:
type: string
description: label for whom the item is (party orders)
CartModifier:
type: object
required:
- modifierId
properties:
groupId:
type: string
modifierId:
type: string
quantity:
type: integer
minimum: 1
default: 1
placement:
type: string
enum:
- whole
- left
- right
default: whole
modifiers:
type: array
items:
$ref: '#/components/schemas/CartModifier'
OrderRequest:
allOf:
- $ref: '#/components/schemas/Cart'
- type: object
required:
- orderType
properties:
orderType:
$ref: '#/components/schemas/OrderType'
requestedTime:
type: string
format: date-time
description: Omit for ASAP. Must be an available slot from /schedule. ASAP on a closed store
is rejected (LOCATION_CLOSED) — never silently rolled to the next day.
deliveryAddress:
$ref: '#/components/schemas/Address'
vehicle:
$ref: '#/components/schemas/Vehicle'
tableNumber:
type: string
tip:
type: object
properties:
amount:
type: integer
description: cents
percent:
type: integer
description: alternative to amount; server computes
promoCode:
type: string
notes:
type: string
maxLength: 250
description: order-level instructions
customer:
$ref: '#/components/schemas/Customer'
payment:
$ref: '#/components/schemas/PaymentRequest'
source:
type: object
properties:
channel:
type: string
enum:
- kiosk
- app
- web
- partner
deviceId:
type: string
partnerOrderId:
type: string
Customer:
type: object
properties:
firstName:
type: string
lastName:
type: string
email:
type: string
format: email
phone:
type: string
description: E.164 or 10 digits; server normalizes (formatted phones must never fail a paid
order)
marketingOptIn:
type: boolean
PaymentRequest:
type: object
required:
- method
properties:
method:
type: string
enum:
- cardToken
- wallet
- cardPresent
- softPos
- savedCard
- giftCard
- payAtStore
cardToken:
type: object
description: Card-not-present token from Location.cardTokenization (e.g. Shift4 i4Go TrueToken).
Server charges it.
properties:
token:
type: string
postalCode:
type: string
wallet:
type: object
properties:
type:
type: string
enum:
- applePay
- googlePay
token:
type: string
cardPresent:
type: object
description: A kiosk pinpad payment already approved via /payments/v1/start. Server verifies
location, amount and single use.
properties:
paymentId:
type: string
softPos:
type: object
description: On-device Datacap (dsiEMVAndroid) result. Only accepted from a device enrolled
for SoftPOS; approvedAmount must equal the server total.
properties:
recordNo:
type: string
authCode:
type: string
refNo:
type: string
approvedAmount:
type: string
description: dollars, e.g. 27.16
cardType:
type: string
acctNoMasked:
type: string
savedCardId:
type: string
giftCards:
type: array
description: Applied first; remaining balance goes to the primary method
items:
type: object
properties:
cardNumber:
type: string
pin:
type: string
amount:
type: integer
description: cents; omit to use max available
loyaltyRewardIds:
type: array
items:
type: string
Quote:
type: object
properties:
quoteId:
type: string
expiresAt:
type: string
format: date-time
orderable:
type: boolean
description: false when issues[] is non-empty
issues:
type: array
items:
$ref: '#/components/schemas/Issue'
lines:
type: array
items:
$ref: '#/components/schemas/PricedLine'
totals:
$ref: '#/components/schemas/Totals'
estimatedReadyTime:
type: string
format: date-time
upsells:
type: array
items:
type: object
properties:
itemId:
type: string
name:
type: string
price:
type: integer
PricedLine:
type: object
properties:
lineId:
type: string
itemId:
type: string
name:
type: string
sizeName:
type: string
quantity:
type: integer
unitPrice:
type: integer
description: cents incl. modifiers
total:
type: integer
modifiers:
type: array
items:
type: object
properties:
modifierId:
type: string
name:
type: string
placement:
type: string
price:
type: integer
specialInstructions:
type: string
Totals:
type: object
description: total = subtotal - discount + fees + deliveryFee + tax + tip. All cents.
properties:
subtotal:
type: integer
discount:
type: integer
fees:
type: array
items:
type: object
properties:
type:
type: string
label:
type: string
amount:
type: integer
deliveryFee:
type: integer
tax:
type: integer
tip:
type: integer
total:
type: integer
giftCardApplied:
type: integer
amountDue:
type: integer
description: total - giftCardApplied - loyalty; what the primary method is charged
PaymentResult:
type: object
properties:
method:
type: string
status:
type: string
enum:
- pending
- approved
- declined
- voided
- refunded
- dueAtStore
amount:
type: integer
brand:
type: string
last4:
type: string
authCode:
type: string
reference:
type: string
description: processor reference (for support/refunds)
Order:
type: object
properties:
orderId:
type: string
description: opaque, non-sequential
orderNumber:
type: string
description: human/restaurant-facing number shown on tickets
accessToken:
type: string
description: returned on create only; lets GET /orders/{id}?token= be used from a customer tracking
link
locationId:
type: string
status:
type: string
enum:
- received
- scheduled
- sentToPos
- confirmed
- ready
- outForDelivery
- completed
- cancelled
- failed
statusReason:
type: string
orderType:
$ref: '#/components/schemas/OrderType'
placedAt:
type: string
format: date-time
requestedTime:
type: string
format: date-time
estimatedReadyTime:
type: string
format: date-time
lines:
type: array
items:
$ref: '#/components/schemas/PricedLine'
removedLines:
type: array
description: Always empty for API orders — sold-out items reject the order (ITEM_UNAVAILABLE)
rather than being silently dropped after payment.
items:
type: string
totals:
$ref: '#/components/schemas/Totals'
payment:
$ref: '#/components/schemas/PaymentResult'
customer:
$ref: '#/components/schemas/Customer'
deliveryAddress:
$ref: '#/components/schemas/Address'
delivery:
type: object
properties:
provider:
type: string
trackingUrl:
type: string
driverName:
type: string
pos:
type: object
properties:
system:
type: string
example: skytab
checkNumber:
type: string
status:
type: string
enum:
- pending
- sent
- acknowledged
- rejected
trackingUrl:
type: string
LoyaltyAccount:
type: object
properties:
enrolled:
type: boolean
points:
type: integer
pointsValue:
type: integer
description: cents
punchCards:
type: array
items:
type: object
properties:
id:
type: string
name:
type: string
punches:
type: integer
threshold:
type: integer
rewards:
type: array
items:
type: object
properties:
id:
type: string
name:
type: string
pointsCost:
type: integer
value:
type: integer
redeemable:
type: boolean
Questions or partner access: [email protected].