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
| Field | Required | Description |
|---|---|---|
location_id | yes | Location to place the order at |
items | yes | At least one item |
items[].id | yes | Inventory item id |
items[].quantity_cents | yes | Quantity × 100 |
items[].variants | no | Variant selections, [{ "id": 12 }] |
items[].price_cents | no | Price override in cents |
items[].coursing_order | no | Course number; 0 sends immediately |
ordering_member | no | user_id, email or phone, plus name for new guests |
vouchers | no | Voucher codes to apply |
order_destination | no | NONE (default, a fresh tab), TAB, QR or KD |
order_destination_id | no | UUID of the destination when it is not NONE |
order_type | no | OTC (default), TAKEAWAY or TABLE |
Errors
| Status | Code | When |
|---|---|---|
| 400 | INVALID_INPUT | Missing required fields or a bad type. The response details.hint names what is missing. |
| 401 | MISSING_API_KEY | No X-API-Key header |
| 403 | INSUFFICIENT_PERMISSIONS | The key lacks inventory.create |
| 403 | SCOPE_VIOLATION | The key is not scoped to this location |
| 429 | RATE_LIMIT_EXCEEDED | See 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.