Skip to main content

Create an order

This guide walks through the recommended flow to create an order through the API: read the menu, resolve the client (and address, for delivery), price delivery if needed, create the order, add its items and payment, and handle the common rejections along the way.

The flow at a glance​

Step 1 — read the menu​

Resolve which product variants the order needs. Either read the full menu (GET /v1/menu) or browse it lean: GET /v1/menu/categories, GET /v1/menu/products, and GET /v1/menu/products/{id} for one product's variants and modifier categories. You need a variant_id (and, for any add-ons, a modifier_id) per item you're about to add to the order.

Required scope: menu:read

Step 2 — find or create the client (and address)​

GET /v1/clients, filtered by phone:

GET /v1/clients?filter[phone_number]=987654321&filter[country_calling_code]=51

If no client matches, create one:

POST /v1/clients
{
"name": "Maria Lopez",
"phone_number": "987654321",
"country_calling_code": "51"
}

Required scopes: clients:read (lookup), clients:write (create).

For a DELIVERY order, resolve a delivery address the same way — list the client's saved addresses or create one:

POST /v1/addresses
{
"client_id": "9c8e720f-31b1-485a-85fa-0aaf552faaa9",
"address": "Av. Larco 345",
"latitude": -12.1218,
"longitude": -77.0297
}

Required scopes: addresses:read (lookup), addresses:write (create).

Step 3 — price delivery (DELIVERY orders only)​

Don't guess the delivery fee: resolve it from the merchant's own pricing configuration. See Calculate delivery prices for the full flow (GET /v1/delivery/pricing + GET /v1/delivery/quote) — it ends with the delivery_price you pass below.

Step 4 — create the order​

POST /v1/orders

Required scope: orders:write. Required header: Idempotency-Key — a unique value per order attempt; retrying the same key with the same body replays the original result instead of creating a second order.

curl -X POST https://public-api.olaclick.app/v1/orders \
-H "Authorization: Bearer olk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 018f1c33-5a2b-7e4d-9c1a-3b6f8e2d4a70" \
-d '{
"service_type": "DELIVERY",
"client_id": "9c8e720f-31b1-485a-85fa-0aaf552faaa9",
"address_id": "6b2f1a04-1c7e-4a2b-9f11-2d8e0b3c4d55",
"delivery_price": 6.5
}'
FieldApplies toNotes
service_typeallDELIVERY, ONSITE, TABLE or TAKEAWAY. Required.
statusallINITIATED (default) or PENDING. See below.
client_idallAn existing client of your company.
table_idTABLE onlyRejected on any other service_type.
address_id, delivery_priceDELIVERY onlyRejected on any other service_type.
comment, order_title, packaging_price, scheduled_delivery_dateallOptional.

The response (201) is the created order. See the REST API Reference for the full response shape.

Draft (INITIATED) vs PENDING​

By default the order is created as INITIATED — a draft the merchant does not see yet, and drafts are never returned by GET /v1/orders: keep the id from the create response and read the draft with GET /v1/orders/{id}. Add its items (and any discounts or coupon) while it's still a draft, then confirm it with PATCH /v1/orders/{id}/status and {"status": "PENDING"} (or PREPARING to skip the merchant's own confirmation step). Confirming assigns the order its daily number and makes it visible to the merchant.

Registering a payment also confirms a draft: the first payment on an INITIATED order (even a partial one) moves it to PENDING and assigns its daily number, without the PATCH. So add items, discounts and coupons first, and the payment last.

To skip the draft entirely, send "status": "PENDING" on creation — the order is confirmed right away.

Step 5 — items, payments, discounts, coupons​

Add every item in one call:

POST /v1/orders/{id}/combos
{
"combos": [
{
"variant_id": "3f6a9c2e-1b4d-4e7a-8c9f-2d5e7a1b3c40",
"quantity": 2,
"modifiers": [{ "modifier_id": "8e2b4d6f-9a0c-4e1b-85d7-9f2a4c6e8b10", "quantity": 1 }]
}
]
}

Then register a payment (payment_method_id comes from GET /v1/company/payment-methods):

POST /v1/orders/{id}/payments
{
"payment_method_id": "4a7c9e1b-3d6f-4b2a-9e8c-1f3a5b7d9c20",
"bill_amount": 25.5
}

A manual discount:

POST /v1/orders/{id}/discounts
{ "type": "PERCENTAGE", "value": 10, "description": "Loyalty 10%" }

Or a coupon code (the order needs a client attached first):

POST /v1/orders/{id}/coupon
{ "code": "WELCOME10" }

Required scope for all of the above: orders:write. All of them work only on orders your company created through the API (combos, discounts); payments also accept orders placed on the storefront or the POS — see each endpoint's reference page for the exact rule.

Common 4xx errors​

StatusCodeWhen
422validation_failedA field doesn't apply to the service_type sent (e.g. table_id on a DELIVERY order), a required field is missing, or a value is out of range (e.g. a discount value over 100 for PERCENTAGE).
400idempotency_key_requiredPOST /v1/orders without an Idempotency-Key header, or with an empty one.
400idempotency_key_invalidThe key is longer than 255 characters.
422idempotency_conflictThe same Idempotency-Key was reused with a different request body.
409idempotency_in_progressA request with that key is still being processed; retry after the Retry-After header.
422order_not_modifiableActing on an order from a channel this endpoint doesn't allow to edit (e.g. adding items to an order placed on a food app).
422validation_failed (field status)Cancelling (POST /v1/orders/{id}/cancel with a reason) an order that still has active payments. Remove the payments first, then cancel.
422coupon_not_applicableThe coupon exists but isn't eligible for this order (usage limit reached, once-per-customer, wrong service type, restricted to new customers).
404not_foundUnknown order id or coupon code.
403insufficient_scopeThe API key is missing a required scope.

Track the order​

Once confirmed, don't poll for status changes — register a webhook and let OlaClick push them to you. See Receive order updates with webhooks.

Doing it through the MCP server​

ToolWhat it does
list_products / get_productBrowse the menu for variant_ids
get_client / create_clientResolve the client
list_addresses / create_addressResolve the delivery address
get_delivery_pricing / get_delivery_quotePrice delivery
create_orderCreate the order (an idempotency_key is generated for you if omitted)
add_order_combosAdd items
update_order_statusConfirm or advance the order
add_order_payment / add_order_discount / apply_order_couponPayments, discounts, coupons

Needed scopes: menu:read, clients:read/clients:write, addresses:read/addresses:write, orders:read/orders:write.

Example prompts:

"Create a delivery order for client 9c8e720f-31b1-485a-85fa-0aaf552faaa9 with 2 of variant 3f6a9c2e-1b4d-4e7a-8c9f-2d5e7a1b3c40, then confirm it"

"Add a 10% loyalty discount to order 7a1e9c3a-4b2d-4f7e-9a1b-3c6d8e2f5a90"

See the MCP Server page for every tool's full parameters.