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 for RTO kiosks, which\n\
    authenticate with a device token. Partner API keys are the next release; until then, partner\naccess\
    \ is arranged directly with Real Time Ordering. Endpoints marked *Planned* are the agreed\ncontract\
    \ and may still change in detail.\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: planned
  /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: planned
  /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
