Real Time Ordering · API reference · v1.0.0

RTO Ordering API

Menus, pricing, ordering and order status for restaurants on Real Time Ordering — built for self-order kiosks, branded apps and ordering partners.

Base URL https://www.gimmegrub.com/api/v1 7 of 16 endpoints live Money in integer cents · ISO-8601 times · JSON
Jump to an endpoint

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. locationId is the restaurant's short name in its ordering URL.
  • Every order write needs an Idempotency-Key header. 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/quote and POST /orders count. 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

EndpointWhat it doesStatus
GET /locations/{locationId}Location profile, order types, fees, tipping and payment optionsLive
GET /locations/{locationId}/menuFull orderable menu for a location and order typeLive
GET /locations/{locationId}/menu/items/{itemId}One item with its full modifier tree (for lazy-loading large menus)Live
GET /locations/{locationId}/scheduleOpen hours and orderable time slots per order typeLive
POST /locations/{locationId}/delivery/quoteIs this address deliverable, and at what fee, minimum and ETA?Planned
POST /orders/quoteValidate and price a cart exactly as POST /orders would, without placing itLive
POST /ordersPlace an orderLive
GET /orders/{orderId}Order status and receiptLive
POST /orders/{orderId}/arrivalCurbside "I'm here"Planned
POST /locations/{locationId}/promotions/validateCheck a coupon code against a cart without placing an orderPlanned
POST /locations/{locationId}/gift-cards/balanceGift card balance lookupPlanned
GET /customers/me/loyaltyPoints balance, punch cards and redeemable rewards at a locationPlanned
POST /customers/tokenExchange customer credentials (or an SMS one-time code) for a customer bearer tokenPlanned
GET /customers/me/ordersOrder history (reorder source)Planned
GET /customers/me/payment-methodsSaved cards (tokens only; brand + last4)Planned
POST order.updatedSent to the client's registered URL whenever an order's status changesPlanned
GET/locations/{locationId}Live

Location profile, order types, fees, tipping and payment options

Parameters

ParameterInTypeNotes
locationId requiredpathstringRestaurant orderUrl

Responses

200OK
{
  "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"
    ]
  }
}
404Unknown location, item or order
GET/locations/{locationId}/menuLive

Full orderable menu for a location and order type

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

ParameterInTypeNotes
locationId requiredpathstringRestaurant orderUrl
orderType querypickup | curbside | delivery | dineIn
at querystring · date-timeEvaluate menu/category availability at this time (future orders). Defaults to now.
If-None-Match headerstring

Responses

200OK
{
  "menuId": "menuId",
  "name": "2pc Snack",
  "currency": "USD",
  "updatedAt": "2026-09-21T18:30:00-07:00",
  "categories": [
    {
      "id": "id",
      "name": "2pc Snack",
      "description": "description",
      "imageUrl": "imageUrl",
      "sortOrder": 1,
      "availability": {
        "windows": "\u2026",
        "orderTypes": "\u2026"
      },
      "items": [
        {
          "id": "\u2026",
          "name": "\u2026"
        }
      ]
    }
  ]
}
304Not modified
404Unknown location, item or order
GET/locations/{locationId}/menu/items/{itemId}Live

One item with its full modifier tree (for lazy-loading large menus)

Parameters

ParameterInTypeNotes
locationId requiredpathstringRestaurant orderUrl
itemId requiredpathstring

Responses

200OK
{
  "id": "id",
  "name": "2pc Snack",
  "description": "description",
  "imageUrl": "imageUrl",
  "price": 995,
  "sizes": [
    {
      "id": "id",
      "name": "2pc Snack",
      "price": 995,
      "available": true,
      "isDefault": true
    }
  ],
  "modifierGroups": [
    {
      "id": "id",
      "name": "2pc Snack",
      "minSelections": 1,
      "maxSelections": 1,
      "maxPerModifier": 1,
      "style": "standard",
      "modifiers": [
        {
          "id": "\u2026",
          "name": "\u2026"
        }
      ]
    }
  ],
  "available": true,
  "availability": {
    "windows": [
      {
        "days": "\u2026",
        "start": "\u2026"
      }
    ],
    "orderTypes": [
      "pickup"
    ]
  },
  "taxRate": "0.075",
  "tags": [
    "alcohol"
  ],
  "minimumAge": 1,
  "maxQuantity": 1,
  "specialInstructionsAllowed": true,
  "posId": "posId"
}
404Unknown location, item or order
GET/locations/{locationId}/scheduleLive

Open hours and orderable time slots per order type

Parameters

ParameterInTypeNotes
locationId requiredpathstringRestaurant orderUrl
orderType requiredquerypickup | curbside | delivery | dineIn
days queryinteger

Responses

200OK
{
  "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"
        }
      ]
    }
  ]
}
POST/locations/{locationId}/delivery/quotePlanned

Is this address deliverable, and at what fee, minimum and ETA?

Stateless: checks an address without touching any cart or order.

Parameters

ParameterInTypeNotes
locationId requiredpathstringRestaurant 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

200OK
{
  "deliverable": true,
  "reason": "outsideZone",
  "fee": 399,
  "minimumOrder": 0,
  "estimatedDeliveryTime": "2026-09-21T18:30:00-07:00",
  "provider": "provider",
  "zoneId": "zoneId"
}
422Cart/order cannot be placed as sent. error.details.issues lists every problem.
POST/orders/quoteLive

Validate 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

200Priced (check 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
    }
  ]
}
404Unknown location, item or order
POST/ordersLive

Place 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

ParameterInTypeNotes
Idempotency-Key requiredheaderstring · 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

201Order accepted
{
  "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"
}
200Idempotent replay of an order already created with this key
{
  "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"
}
402PAYMENT_DECLINED / PAYMENT_AMOUNT_MISMATCH / PAYMENT_NOT_APPROVED. Nothing sent to the POS.
409PRICE_CHANGED (details.quote has new totals), IDEMPOTENCY_CONFLICT, or PAYMENT_ALREADY_USED
422Cart/order cannot be placed as sent. error.details.issues lists every problem.
502POS refused the order; payment already voided/refunded (see details.payment).
GET/orders/{orderId}Live

Order 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

ParameterInTypeNotes
orderId requiredpathstring

Responses

200OK
{
  "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"
}
404Unknown location, item or order
POST/orders/{orderId}/arrivalPlanned

Curbside "I'm here"

Auth: apiKey, orderToken

Parameters

ParameterInTypeNotes
orderId requiredpathstring

Request body

{
  "vehicle": {
    "make": "make",
    "model": "model",
    "color": "color",
    "plate": "plate"
  },
  "parkingSpot": "parkingSpot"
}

Responses

204Merchant notified
POST/locations/{locationId}/promotions/validatePlanned

Check a coupon code against a cart without placing an order

Parameters

ParameterInTypeNotes
locationId requiredpathstringRestaurant orderUrl

Request body

{
  "code": "ITEM_UNAVAILABLE",
  "cart": {
    "locationId": "nancyjosburgers",
    "lines": [
      {
        "lineId": "\u2026",
        "itemId": "\u2026"
      }
    ]
  },
  "email": "[email protected]"
}

Responses

200OK
{
  "valid": true,
  "discount": 0,
  "description": "description",
  "reason": "reason"
}
POST/locations/{locationId}/gift-cards/balancePlanned

Gift card balance lookup

Parameters

ParameterInTypeNotes
locationId requiredpathstringRestaurant orderUrl

Request body

{
  "cardNumber": "cardNumber",
  "pin": "pin"
}

Responses

200OK
{
  "balance": 2500,
  "currency": "USD"
}
422Cart/order cannot be placed as sent. error.details.issues lists every problem.
429Too many requests; honour Retry-After
GET/customers/me/loyaltyPlanned

Points balance, punch cards and redeemable rewards at a location

Auth: customerToken

Parameters

ParameterInTypeNotes
locationId requiredquerystring

Responses

200OK
{
  "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
    }
  ]
}
POST/customers/tokenPlanned

Exchange 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

200OK
{
  "accessToken": "accessToken",
  "expiresAt": "2026-09-21T18:30:00-07:00",
  "customer": {
    "firstName": "Sam",
    "lastName": "Rivera",
    "email": "[email protected]",
    "phone": "(503) 555-0142",
    "marketingOptIn": true
  }
}
401Missing/invalid credentials
GET/customers/me/ordersPlanned

Order history (reorder source)

Auth: customerToken

Parameters

ParameterInTypeNotes
locationId querystring

Responses

200OK
{
  "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"
    }
  ]
}
GET/customers/me/payment-methodsPlanned

Saved cards (tokens only; brand + last4)

Auth: customerToken

Responses

200OK
[
  {
    "id": "id",
    "brand": "VISA",
    "last4": "1111",
    "expMonth": 1,
    "expYear": 1
  }
]
POSTorder.updatedPlanned

Sent 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

2XXAcknowledged

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_LIMITEDINTERNAL

OpenAPI 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].