Skip to content

Orders

The Orders API handles order calculation, creation, and tracking.

Calculate Order

Calculate order totals including tax before submitting.

POST /v1/ecommerce/orders/calculate

Authentication: OAuth Token

Scope: kitchenclick:orders.calculate

Rate Limit: 30/min per client

Request Body

json
{
  "location_id": "loc_00000k1L2m3N4o5",
  "order_type": "pickup",
  "items": [
    {
      "item_id": "itm_00000a1B2c3D4e5",
      "quantity": 2,
      "special_instructions": "No onions on one",
      "modifiers": [
        { "modifier_id": "mod_cheddar", "quantity": 1 },
        { "modifier_id": "mod_bacon", "quantity": 1 }
      ]
    }
  ],
  "tip_amount": 5.00,
  "save_quote": true
}

Request Fields

FieldTypeRequiredDescription
location_idstringYes¹Target location ID
order_typestringYes¹One of the accepted order types
itemsarrayYes¹Order items
items[].item_idstringYesMenu item ID
items[].quantityintegerYesQuantity (≥ 1)
items[].special_instructionsstringNoItem-level note (≤ 255)
items[].modifiersarrayNoSelected modifiers
items[].modifiers[].modifier_idstringYesModifier ID
items[].modifiers[].quantityintegerNoModifier quantity
items[].modifiers[].childrenarrayNoNested modifiers — { modifier_id, quantity }
customerobjectNo{ name, phone, email }
fulfillment_methodstringCond.pickup or delivery; required when order_type is catering
scheduled_atstringNoISO 8601 future time (store-local)
delivery_infoobjectCond.Required for delivery{ address, address_line2, city, state, zip, instructions }
tip_amountnumberNoFlat tip amount
tip_percentagenumberNoTip as a percent of subtotal (0–100)
delivery_feenumberNoDelivery fee, e.g. the chosen quote fee from Delivery
special_instructionsstringNoOrder-level note (≤ 500)
save_quotebooleanNoWhen true, returns a reusable quote_token_id
quote_token_idstringNoRe-price an existing saved quote instead of a fresh calculation
quote_validity_minutesintegerNoQuote lifetime, 5–60 (default 15)

¹ location_id, order_type, and items are required for a fresh calculation. When quote_token_id is supplied they are optional (only the fields you send are changed on the quote).

Response

json
{
  "status": "success",
  "data": {
    "items": [
      {
        "item_id": "itm_00000a1B2c3D4e5",
        "name": "Classic Burger",
        "quantity": 2,
        "unit_price": 14.99,
        "subtotal": 29.98,
        "tax": 1.87,
        "deposits": 0,
        "modifiers": [
          { "modifier_id": "mod_bacon", "name": "Bacon", "price": 2.00, "quantity": 1 }
        ],
        "is_market_priced": false,
        "price_status": "set"
      }
    ],
    "totals": {
      "subtotal": 29.98,
      "tax_amount": 1.87,
      "deposits_amount": 0,
      "fees_amount": 0,
      "delivery_fee": 0,
      "tip_amount": 5.00,
      "total": 36.85
    },
    "breakdown": {
      "tax": [
        { "id": "tax_rate_1", "name": "Sales Tax", "pos_tax_code": "1", "rate": 0.0625, "taxable_amount": 29.98, "amount": 1.87 }
      ],
      "deposits": [],
      "fees": []
    },
    "quote": {
      "quote_token_id": "kqt_00000a1B2c3D4e5",
      "expires_at": "2026-06-17T14:30:00Z",
      "validity_minutes": 15,
      "updated": false
    }
  }
}

Response Notes

FieldNotes
totalsAll amounts are decimal dollars. total = subtotal + tax + deposits + fees + delivery_fee + tip.
items[].is_market_pricedtrue for market-priced items whose price is set in-kitchen; these contribute 0 until priced (price_status: "pending").
breakdownItemized tax, deposits, and fees arrays behind the totals.
quotePresent only when save_quote is true (or when updating a quote). Pass quote.quote_token_id to Create Stripe Payment Intent and the order-create endpoints.
cateringAn additional catering object (deposit_percentage, deposit_amount, balance_after_deposit, allow_deposit) is included when order_type is catering.

Create Order

Submit a new order for processing.

POST /v1/ecommerce/orders

Which order-creation endpoint should I use?

  • POST /v1/ecommerce/orders/stripe — the standard online path: create an order after a Stripe card payment has been confirmed client-side (pass the confirmed payment_intent_id). Use this for card checkout. See Payments.
  • POST /v1/ecommerce/orders (this endpoint) — create an order without an attached online card payment, e.g. pay-in-store.
  • Payment presentment (Payments) is for in-person terminals/kiosks, not browser checkout.

For the full sequence, see the End-to-End Integration Flow.

Authentication: OAuth Token

Scope: kitchenclick:orders.create

Rate Limit: 30/min per client

Request Body

json
{
  "location_id": "loc_00000k1L2m3N4o5",
  "order_type": "pickup",
  "customer": {
    "name": "John Doe",
    "phone": "(555) 123-4567",
    "email": "john.doe@example.com",
    "notify_on_ready": true
  },
  "scheduled_at": null,
  "special_instructions": "Extra napkins please",
  "items": [
    {
      "item_id": "itm_00000a1B2c3D4e5",
      "quantity": 2,
      "special_instructions": "No onions on one",
      "modifiers": [
        { "modifier_id": "mod_cheddar", "quantity": 1 }
      ]
    }
  ],
  "payment_method": "cash",
  "tip_amount": 5.00
}

Create from a saved quote

Instead of resending all order details, you can submit just { "quote_token_id": "kqt_…", "customer": { … } } using a quote_token_id returned by Calculate Order.

Request Fields

FieldTypeRequiredDescription
location_idstringYesTarget location ID
order_typestringYesOne of the accepted order types
customerobjectNoCustomer info
customer.namestringNoCustomer name
customer.phonestringNoContact phone
customer.emailstringNoEmail for receipt
customer.notify_on_readybooleanNoSend a ready notification
concept_idstringNoConcept ID (required for catering orders)
scheduled_atstringNoISO 8601 future time for scheduled orders
special_instructionsstringNoOrder-level note (≤ 500)
itemsarrayYesOrder items (same shape as Calculate Order)
items[].item_idstringYesMenu item ID
items[].quantityintegerYesQuantity (≥ 1)
items[].special_instructionsstringNoItem-level note (≤ 255)
items[].modifiers[].modifier_idstringYesModifier ID
delivery_infoobjectCond.Required for delivery orders (see below)
payment_methodstringNocredit_card, cash, or digital_wallet
tip_amountnumberNoTip amount

Alternatively, send quote_token_id (+ customer) to create the order from a saved quote.

Delivery Info

For delivery orders, include a delivery_info object:

json
{
  "delivery_info": {
    "address": "456 Oak Avenue",
    "address_line2": "Apt 2B",
    "city": "Austin",
    "state": "TX",
    "zip": "78702",
    "instructions": "Gate code: 1234. Leave at door."
  }
}

Response

json
{
  "status": "success",
  "message": "Order placed successfully",
  "data": {
    "order_id": "ord_00000x1Y2z3A4b5",
    "order_number": "1247",
    "status": "confirmed",
    "order_type": "takeout",
    "channel": "ecommerce",
    "placed_at": "2026-06-17T14:30:00Z",
    "estimated_ready_time": "2026-06-17T14:45:00Z",
    "scheduled_at": null,
    "totals": {
      "subtotal": 29.98,
      "tax": 2.22,
      "delivery_fee": 0,
      "tip": 5.00,
      "total": 37.20
    },
    "tracking_url": "https://api.kitchenclick.retailsuccessplatform.com/api/v1/ecommerce/orders/ord_00000x1Y2z3A4b5/track"
  }
}
FieldTypeDescription
order_idstringOrder ID (prefix ord_) — use for tracking
order_numberstringHuman-readable order number
statusstringconfirmed (ASAP) or scheduled (future order, staged for firing)
order_typestringCanonical stored type (e.g. takeout, dine_in, delivery)
channelstringecommerce, or kiosk when the X-Kiosk-ID header is supplied
totalsobjectDecimal-dollar amounts: subtotal, tax, delivery_fee, tip, total
tracking_urlstringAbsolute URL to the public tracking endpoint

Customer Order History

Retrieve past orders for a customer by email address. Useful for showing order history or enabling quick reorder.

GET /v1/ecommerce/customers/orders

Authentication: OAuth Token

Scope: kitchenclick:orders.calculate

Rate Limit: 200/min per client

Query Parameters

ParameterTypeRequiredDescription
emailstringYesCustomer email address
location_idstringNoFilter by location hashkey
per_pageintegerNoResults per page (1-50, default: 20)

Response

json
{
  "status": "success",
  "data": [
    {
      "order_id": "ord_00000x1Y2z3A4b5",
      "order_number": "#1247",
      "status": "completed",
      "order_type": "pickup",
      "date": "2024-01-15T14:30:00Z",
      "total": 34.20,
      "item_summary": "Classic Burger, Side Salad, Iced Tea",
      "items_count": 3
    },
    {
      "order_id": "ord_00000c6D7e8F9g0",
      "order_number": "#1198",
      "status": "completed",
      "order_type": "delivery",
      "date": "2024-01-10T18:15:00Z",
      "total": 52.75,
      "item_summary": "BBQ Chicken Pizza, Caesar Salad, Garlic Bread +2 more",
      "items_count": 5
    }
  ],
  "meta": {
    "total": 42,
    "per_page": 20,
    "current_page": 1,
    "last_page": 3
  }
}

Response Fields

FieldTypeDescription
order_idstringOrder hashkey
order_numberstringHuman-readable order number
statusstringOrder status (cancelled orders are excluded)
order_typestringpickup, delivery, dine-in, etc.
datestringISO 8601 timestamp when order was placed
totaldecimalOrder total amount
item_summarystringFirst 3 item names, then "+X more" if applicable
items_countintegerTotal number of items in the order
metaobjectPagination metadata

Track Order

Get current order status. This endpoint is public - no authentication required.

GET /v1/ecommerce/orders/{order}/track

Authentication: None (Public)

Rate Limit: 100/min per IP

Path Parameters

ParameterTypeDescription
orderstringOrder hashkey

Response

json
{
  "status": "success",
  "data": {
    "order_number": "1247",
    "status": "preparing",
    "order_type": "takeout",
    "placed_at": "2024-01-15T14:30:00Z",
    "estimated_ready_time": "2024-01-15T14:45:00Z",
    "status_history": [
      {
        "status": "pending",
        "timestamp": "2024-01-15T14:30:00Z"
      },
      {
        "status": "confirmed",
        "timestamp": "2024-01-15T14:30:15Z"
      },
      {
        "status": "preparing",
        "timestamp": "2024-01-15T14:32:00Z"
      }
    ],
    "items": [
      {
        "name": "Classic Burger",
        "quantity": 2,
        "status": "preparing"
      }
    ]
  }
}

Order Statuses

StatusDescription
pendingOrder received, awaiting confirmation
confirmedOrder confirmed, queued for kitchen
preparingKitchen is preparing the order
readyOrder is ready for pickup/delivery
out_for_deliveryDriver has picked up (delivery only)
completedOrder fulfilled
cancelledOrder was cancelled

Order Types

order_type accepts the values below. Inputs are normalized to a canonical stored value, so the ecommerce aliases (e.g. pickup, dinein) are accepted and map to the canonical form.

Input valueCanonicalDescription
pickup, takeouttakeoutCustomer picks up
deliverydeliveryDelivered to customer address
dinein, dine-in, dine_indine_inDine in at location
drivethru, drive-thru, drive_through, drive_thrudrive_thruDrive-thru pickup
curbsidecurbsideCurbside pickup
cateringcateringCatering order (deposit/invoice flow)
special_orderspecial_orderSpecial order

Scheduled Orders

To place an order for a future time, set scheduled_at (and, for delivery/catering, the relevant fulfillment fields):

json
{
  "scheduled_at": "2026-06-18T18:00:00Z",
  "order_type": "pickup"
}
  • scheduled_at must be in the future.
  • Allowed lead time, increments, and how far ahead orders may be placed are configured per location/concept — fetch them from the scheduling-config and time-slots endpoints and present only valid slots.
  • Scheduled orders are created with status scheduled and fired to the kitchen at the appropriate time.

Error Responses

Item Unavailable

json
{
  "status": "error",
  "message": "One or more items are unavailable",
  "errors": {
    "items": [
      {
        "item_id": "itm_00000a1B2c3D4e5",
        "error": "Item is currently 86'd (out of stock)"
      }
    ]
  }
}

Invalid Modifiers

json
{
  "status": "error",
  "message": "Invalid modifier selection",
  "errors": {
    "items.0.modifiers": [
      "Option group 'Choose Your Cheese' requires exactly 1 selection"
    ]
  }
}

Location Closed

json
{
  "status": "error",
  "message": "Location is currently closed",
  "errors": {
    "location": ["Location is not accepting orders at this time"]
  }
}

Example: Complete Order Flow

javascript
async function submitOrder(locationId, cart, customer, paymentMethod) {
  const token = await tokenManager.getToken();

  // 1. Calculate totals first
  const calcResponse = await fetch('/api/v1/ecommerce/orders/calculate', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      location_id: locationId,
      order_type: 'pickup',
      items: cart.items.map(item => ({
        item_id: item.id,
        quantity: item.quantity,
        modifiers: item.selectedModifiers.map(m => ({
          modifier_id: m.id,
          quantity: 1,
        })),
      })),
    }),
  });

  const calculation = await calcResponse.json();

  if (calculation.status !== 'success') {
    throw new Error(calculation.message);
  }

  // 2. Show totals to customer, get confirmation
  const confirmed = await showOrderSummary(calculation.data);
  if (!confirmed) return null;

  // 3. Submit the order
  const orderResponse = await fetch('/api/v1/ecommerce/orders', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      location_id: locationId,
      customer: {
        name: customer.name,
        phone: customer.phone,
        email: customer.email,
      },
      order_type: 'pickup',
      items: cart.items.map(item => ({
        item_id: item.id,
        quantity: item.quantity,
        special_instructions: item.notes,
        modifiers: item.selectedModifiers.map(m => ({
          modifier_id: m.id,
          quantity: 1,
        })),
      })),
      payment_method: paymentMethod,
      tip_amount: cart.tip,
    }),
  });

  const order = await orderResponse.json();

  if (order.status !== 'success') {
    throw new Error(order.message);
  }

  return order.data;
}

// 4. Poll for status updates
async function pollOrderStatus(orderHashkey, onUpdate) {
  const poll = async () => {
    const response = await fetch(
      `/api/v1/ecommerce/orders/${orderHashkey}/track`
    );
    const { data } = await response.json();

    onUpdate(data);

    if (!['completed', 'cancelled'].includes(data.status)) {
      setTimeout(poll, 10000); // Poll every 10 seconds
    }
  };

  poll();
}

Changelog
DateChange
2026-06-17Corrected Calculate/Create request and response shapes to match the API (location_id/item_id/modifier_id field names, delivery_info, the quote_token_id flow, real totals/breakdown shape); fixed the order-type values; removed the unsupported promo_code field.
2026-03-14Added e-commerce API endpoints.
2026-01-15Initial publication.

ShopHero CommerceCore Platform