You are viewing a non-production environment — the API surface may differ from production.

Placing an order

Create an order at a Karma location from an external system in a single call.

POST /api/v1/public/orders/create places an order at a location in one request: no tab to pre-create, no guest lookup, no payload assembly. A new tab is opened, a kitchen order is created, and an anonymous guest is generated unless you identify one.

The API key needs the inventory.create permission for the location.

The simplest order

curl -X POST "$COMMON_API/api/v1/public/orders/create" \
  -H "X-API-Key: $KARMA_API_KEY" -H 'Content-Type: application/json' \
  -d '{
    "location_id": 1234,
    "items": [{ "id": 456, "quantity_cents": 100 }]
  }'
{
  "status": "success",
  "data": {
    "orderId": "e01dcd36-cdec-41e5-8708-1b57d943367d",
    "tabId": "59e4ce7d-b801-4323-af9d-ba0ca4bd757a",
    "totalAmountCents": 9900
  }
}

orderId is the kitchen order, tabId the tab it was placed on. Keep the tabId to add more items to the same tab before payment.

Common scenarios

Identify the guest. Pass ordering_member with a user_id, an email or a phone, in that order of priority. An email or phone that matches an existing Karma account reuses it; otherwise a guest is created. name is used for new guests.

{
  "location_id": 1234,
  "ordering_member": { "email": "guest@example.com", "name": "Alex" },
  "items": [{ "id": 456, "quantity_cents": 100 }]
}

Variants, a voucher, takeaway.

{
  "location_id": 1234,
  "order_type": "TAKEAWAY",
  "items": [
    { "id": 456, "quantity_cents": 100, "variants": [{ "id": 12 }, { "id": 34 }] },
    { "id": 789, "quantity_cents": 100 }
  ],
  "vouchers": ["WELCOME10"]
}

Add to an existing tab. Use the tabId from an earlier response:

{
  "location_id": 1234,
  "order_destination": "TAB",
  "order_destination_id": "59e4ce7d-b801-4323-af9d-ba0ca4bd757a",
  "items": [{ "id": 789, "quantity_cents": 100 }]
}

Fractional quantities. quantity_cents is the quantity times 100: 100 is one unit, 50 is half, 200 is two. Use it for weight-based or bulk items.

Price override. price_cents on an item replaces the catalogue price for this order only.

Request reference

FieldRequiredDescription
location_idyesLocation to place the order at
itemsyesAt least one item
items[].idyesInventory item id
items[].quantity_centsyesQuantity × 100
items[].variantsnoVariant selections, [{ "id": 12 }]
items[].price_centsnoPrice override in cents
items[].coursing_ordernoCourse number; 0 sends immediately
ordering_membernouser_id, email or phone, plus name for new guests
vouchersnoVoucher codes to apply
order_destinationnoNONE (default, a fresh tab), TAB, QR or KD
order_destination_idnoUUID of the destination when it is not NONE
order_typenoOTC (default), TAKEAWAY or TABLE

Errors

StatusCodeWhen
400INVALID_INPUTMissing required fields or a bad type. The response details.hint names what is missing.
401MISSING_API_KEYNo X-API-Key header
403INSUFFICIENT_PERMISSIONSThe key lacks inventory.create
403SCOPE_VIOLATIONThe key is not scoped to this location
429RATE_LIMIT_EXCEEDEDSee rate limits

Once the order exists, the location's order.created webhook fires, and tab.payment_completed follows when it is paid. See Receiving webhooks.

On this page