Skip to Content
Orders

Orders

Use the Orders API to import sales from another system, create orders from a custom checkout, track fulfillment, record partial or full payment, schedule future orders, and retrieve receipts.

All endpoints use the /v1/orders prefix and authenticate with an API key. Amounts are expressed in the business currency as decimal numbers, not minor units.

Endpoints

MethodEndpointScopePurpose
GET/v1/ordersorders:readList and filter orders
GET/v1/orders/:idorders:readRetrieve one order
POST/v1/ordersorders:writeCreate an order
PATCH/v1/orders/:idorders:writeUpdate status, payment, customer, or scheduling
POST/v1/orders/:id/confirmorders:writeConfirm a scheduled order
POST/v1/orders/:id/send-to-kitchenorders:writeSend an order to the kitchen
POST/v1/orders/:id/kitchen-statusorders:writeMove an order’s kitchen ticket to a prep stage
POST/v1/orders/:id/requeryorders:readRefresh an automated payment
POST/v1/orders/:id/receiptorders:readGenerate a receipt URL
DELETE/v1/orders/:idorders:writeDelete an order and restore inventory

Order resource

Order reads and successful create, update, confirmation, and payment refresh operations return the same order shape.

{ "id": "5037c85a-44c6-4c3a-bd7d-3e21e73915e1", "shortCode": "482193", "status": "pending", "paymentStatus": "partial", "summary": "2x Jollof Rice", "items": [ { "id": "2b783a34-5b8b-43b7-a441-dcb8ae7db843", "productId": "7a59174f-3207-42a6-8fab-5b8d8b2929fb", "variantId": "f91718fb-1487-4ada-a44c-794b1ba5a3d3", "productName": "Jollof Rice", "variantName": "Large", "quantity": 2, "unitPrice": 3500, "originalUnitPrice": 4000, "discountAmount": 1000, "subtotalAmount": 7000, "taxAmount": 0, "totalPrice": 7000, "imageUrl": "https://cdn.example.com/jollof-rice.jpg", "modifiers": [ { "modifierGroupId": "4f7f9ff4-19e4-4cbb-b7c6-45275e70fc6f", "modifierOptionId": "f494ef42-178a-4e16-bcf2-481a52d55833", "name": "Extra chicken", "priceDelta": 1000 } ], "components": [] } ], "totalAmount": 7000, "subtotalAmount": 8000, "discountAmount": 1000, "serviceChargeAmount": 0, "amountPaid": 4000, "remainingAmount": 3000, "currency": "NGN", "paymentMethod": "transfer", "customer": { "id": "3cadb42d-571b-4979-94b4-c094c60820c4", "name": "Ada Lovelace", "phoneNumber": "+2348012345678", "whatsappNumber": "+2348012345678" }, "store": { "id": "837ceae4-43c2-4bbb-830c-1099d36b9d05", "name": "Lekki" }, "channel": "checkout", "channelFulfilledAt": null, "isAutomated": false, "deposit": null, "scheduledFor": null, "scheduledConfirmedAt": null, "serviceMode": "takeaway", "tableSessionId": null, "tableLabel": null, "kitchenTicketId": null, "kitchenTicketStatus": null, "recordedBy": { "id": "4e3d75bb-4208-4dd4-a292-2c27ecb99185", "name": "API integration" }, "servedBy": null, "tags": [], "notes": "Pack cutlery separately", "receiptUrl": null, "cancellation": null, "occurredAt": "2026-07-14T10:30:00.000Z", "date": "2026-07-14T10:30:00.000Z", "createdAt": "2026-07-14T10:30:02.000Z", "entrySource": "live", "recoverySessionId": null, "academicTerm": null }

Status values

StatusMeaning
scheduledCreated for future fulfillment and not yet confirmed
pendingCreated but not fully paid or otherwise awaiting completion
completedFully paid or explicitly completed
cancelledCancelled with an optional recorded reason
refundedPreviously refunded through a supported refund workflow

paymentStatus is calculated from the order totals:

Payment statusRule
pendingamountPaid is zero
partialamountPaid is greater than zero and less than totalAmount
paidamountPaid is equal to or greater than totalAmount

Setting an order status to refunded does not move money. The public update endpoint therefore does not accept refunded. Use the applicable payment provider refund workflow, then consume the resulting order or transaction update.

List orders

GET /v1/orders?status=pending,scheduled&channel=whatsapp,voice&limit=25

Query parameters

Comma-separated filters use lowercase values.

ParameterTypeDescription
statusenum listscheduled, pending, completed, cancelled, refunded
payment_statusenum listpaid, partial, pending
channelenum listFilter by one or more order channels
service_modeenum listdine_in, takeaway, room_service
payment_methodenumcash, transfer, pos, cloove_pay, credit, other
customer_iduuidOrders linked to a customer
store_iduuidOrders for one store, including business-level orders
store_idsuuid listComma-separated store IDs
recorded_by_iduuidOrders created by a user
staff_iduuidOrders recorded or served by a user
served_by_user_iduuidOrders assigned to a server or waiter
table_session_iduuidOrders charged to a restaurant table session
academic_term_iduuidOrders associated with an academic term
tag_idsuuid listOrders containing any listed tag
start_dateISO date or timestampOrders occurring on or after this value
end_dateISO date or timestampOrders occurring on or before this value
searchstringMatch an order short code or customer name
is_automatedbooleanWhether the order has an automated deposit
channel_fulfilledbooleanWhether a channel order has been treated
scheduled_onlybooleanReturn scheduled orders in fulfillment order
pageintegerPage number, default 1
limitintegerItems per page, default 50, maximum 100

Response

{ "message": "Orders retrieved", "data": [], "meta": { "total": 0, "page": 1, "perPage": 25, "totalPages": 1, "hasMore": false }, "summary": { "todayOrders": 4, "todayRevenue": 42000, "totalOrders": 138, "filteredOrders": 12, "totalRevenue": 184000, "averageOrderValue": 15333.33, "pendingOrdersCount": 6, "pendingOutstandingAmount": 27000, "completedOrdersCount": 7, "completedRevenue": 157000, "cancelledOrdersCount": 1, "cancelledRevenue": 0 } }

filteredOrders and the revenue fields respect the supplied filters. totalOrders and pendingOrdersCount are business workload counts and are not reduced by every list filter.

Retrieve an order

GET /v1/orders/5037c85a-44c6-4c3a-bd7d-3e21e73915e1

This endpoint returns the complete order resource, including line items, customer, store, payment, scheduling, restaurant, staff, deposit, and cancellation data.

A missing order, a deleted order, or an order belonging to another business returns:

{ "error": "not_found", "message": "Order not found." }

Create an order

POST /v1/orders Idempotency-Key: 90ed7ca0-dd71-4550-8bea-62b99d551d41 Content-Type: application/json

Always use a unique UUID as the Idempotency-Key. Repeating a successful request with the same key returns the original order with 200 OK. A new order returns 201 Created.

Core fields

FieldTypeRequiredDescription
itemsarrayYesOne or more product or fee lines
payment_methodenumNoDefaults to cash
amount_paidnumberNoAmount already collected
discount_amountnumberNoOrder-level discount
promotion_iduuidNoPromotion applied to the order
customer_iduuidNoExisting customer
customer_namestringNoFinds or creates a customer when no ID is supplied
store_iduuidNoStore used for inventory and reporting
store_namestringNoStore name when integrating by name
channelenumNoSource channel for the order
tagsstring arrayNoTag IDs, slugs, or names
notesstringNoOrder note, up to 2,000 characters
scheduled_forISO timestampNoFuture fulfillment time
occurred_atISO timestampNoOriginal sale time when importing historical orders

Valid channels are in_person, whatsapp, instagram, tiktok, telegram, twitter, storefront, dashboard, checkout, voice, and other.

Line item fields

FieldTypeRequiredDescription
product_namestringYesDisplay name captured on the order
quantitynumberYesPositive quantity
product_iduuidProduct linesCatalog product used for pricing and inventory
variant_iduuidNoSelected product variant
custom_pricenumberNoUnit price override
line_typeenumNoproduct or fee, default product
modifiersarrayNoSelected modifier options

Catalog-backed lines should include product_id. Fee lines can omit it and use line_type: "fee" with a custom_price.

Modifier fields

FieldTypeRequiredDescription
modifier_group_iduuidYesProduct modifier group
modifier_option_iduuidYesSelected option
namestringYesSnapshot label shown on the order
price_deltanumberNoExpected price addition; server catalog rules remain authoritative

Restaurant and hotel fields

FieldTypeDescription
service_modeenumdine_in, takeaway, or room_service
service_charge_amountnumberService charge applied to the order
table_session_iduuidActive restaurant table session
table_labelstringTable label when no session is supplied
coversintegerNumber of guests
send_to_kitchenbooleanRoute the order to the kitchen workflow and create its kitchen ticket
served_by_user_iduuidServer or waiter responsible for the order
hotel_reservation_iduuidReservation receiving the order
hotel_room_iduuidRoom receiving the order

School field

academic_term_id associates the order with an academic term. It is optional for other business types.

Product and variant example

{ "items": [ { "product_id": "7a59174f-3207-42a6-8fab-5b8d8b2929fb", "variant_id": "f91718fb-1487-4ada-a44c-794b1ba5a3d3", "product_name": "Jollof Rice", "quantity": 2, "modifiers": [ { "modifier_group_id": "4f7f9ff4-19e4-4cbb-b7c6-45275e70fc6f", "modifier_option_id": "f494ef42-178a-4e16-bcf2-481a52d55833", "name": "Extra chicken", "price_delta": 1000 } ] } ], "payment_method": "transfer", "amount_paid": 4000, "discount_amount": 1000, "customer_id": "3cadb42d-571b-4979-94b4-c094c60820c4", "store_id": "837ceae4-43c2-4bbb-830c-1099d36b9d05", "channel": "checkout", "service_mode": "takeaway", "notes": "Pack cutlery separately" }

send_to_kitchen defaults to true for a restaurant or hotel business, or any order with a service_mode or service_charge_amount — the order above is sent to the kitchen immediately without needing to set it. Pass send_to_kitchen: false to hold an order back, for example while its bank transfer payment is still pending, then call Send order to kitchen once it clears:

{ "items": [ { "product_id": "7a59174f-3207-42a6-8fab-5b8d8b2929fb", "product_name": "Jollof Rice", "quantity": 2 } ], "payment_method": "transfer", "amount_paid": 0, "service_mode": "takeaway", "send_to_kitchen": false }

Fee line example

{ "items": [ { "product_name": "Delivery fee", "quantity": 1, "custom_price": 1500, "line_type": "fee" } ], "payment_method": "cash" }

Scheduled order example

{ "items": [ { "product_id": "7a59174f-3207-42a6-8fab-5b8d8b2929fb", "product_name": "Event platter", "quantity": 3 } ], "scheduled_for": "2026-08-20T18:30:00.000Z", "customer_name": "Grace Hopper", "channel": "voice" }

scheduled_for must be a valid future timestamp. The new order enters scheduled status.

Update an order

PATCH /v1/orders/5037c85a-44c6-4c3a-bd7d-3e21e73915e1 Content-Type: application/json

Only supplied fields are changed.

FieldTypeDescription
statusenumscheduled, pending, completed, or cancelled
customer_iduuidAssign an existing customer
customer_namestringFind or create a customer by name
payment_methodenumChange the recorded payment method
amount_paidnumberSet the cumulative amount collected
total_amountnumberCorrect the order total
academic_term_iduuid or nullAssign or clear an academic term
scheduled_forISO timestamp or nullSchedule a recent order or update its schedule
cancellation_reasonstringReason stored when changing to cancelled
channel_fulfilledbooleanMark a WhatsApp or Voice order treated or reopen it

Record a partial payment

{ "payment_method": "transfer", "amount_paid": 4000 }

amount_paid is cumulative. If an order total is 7000 and a second payment brings the collected amount to 7000, send 7000, not the payment delta.

Cancel an order

{ "status": "cancelled", "cancellation_reason": "Customer changed their mind" }

The response includes the stored cancellation audit block. Completed automated bank transfer orders cannot be cancelled through this endpoint because a payment reversal may be required.

Mark a channel order fulfilled

{ "channel_fulfilled": true }

Send false to return the order to the channel fulfillment queue.

Confirm a scheduled order

POST /v1/orders/5037c85a-44c6-4c3a-bd7d-3e21e73915e1/confirm Idempotency-Key: b802662c-3b05-4f9b-859c-14468eb6d485 Content-Type: application/json
{ "payment_method": "pos", "amount_paid": 7000 }
FieldTypeDescription
payment_methodenumFinal payment method
amount_paidnumberAmount collected; defaults to the full order total

A fully paid order becomes completed. A partially paid order becomes pending. Only orders currently in scheduled status can be confirmed.

Send order to kitchen

POST /v1/orders/5037c85a-44c6-4c3a-bd7d-3e21e73915e1/send-to-kitchen Idempotency-Key: 6c2e1a4f-9d8b-4a3e-b7c6-5f1e8a2d3c90

No request body. Creates the order’s kitchen ticket — the same effect as the dashboard’s Send to Kitchen button, including the initial-stage customer WhatsApp notification and a kitchen_ticket.created webhook.

Use it for an order created with send_to_kitchen: false, the usual case for a business that only accepts bank transfer: the order is recorded immediately so payment can be chased, but kitchen prep shouldn’t start until a payment webhook confirms it cleared. Calling this endpoint is that trigger.

An order created with send_to_kitchen: true (the default) already has a kitchen ticket and does not need this call. If the order already has one — check kitchenTicketId — this endpoint returns 409.

Response

{ "message": "Order sent to kitchen", "data": { "id": "5037c85a-44c6-4c3a-bd7d-3e21e73915e1", "shortCode": "482193", "kitchenTicketId": "d2f6a1c4-8b3e-4f7a-9e1d-6c2b5a8f3e90", "kitchenTicketStatus": "queued", "...": "remaining fields match the order object" }, "meta": { "notification": { "status": "sent", "customerName": "Ada Lovelace", "customerPhone": "+2348012345678", "messageId": "wamid.HBg..." } } }

data is the full order resource. meta.notification reports the initial-stage WhatsApp message the same way Update kitchen status’s response does — see that section’s notification.status and notification.reason tables, which apply here unchanged.

Idempotency

Repeating a request with the same Idempotency-Key returns the original response and creates no second ticket. Without a key, a repeat call is not silently absorbed the way kitchen-status absorbs an unchanged status — it returns 409, because a second real ticket is never created for one order:

{ "error": "request_failed", "message": "This order has already been sent to the kitchen." }

Update kitchen status

POST /v1/orders/5037c85a-44c6-4c3a-bd7d-3e21e73915e1/kitchen-status Idempotency-Key: 4b1d2c8e-7f3a-4e9b-9c2d-1a6f5e8b3c70 Content-Type: application/json
{ "status": "preparing" }
FieldTypeDescription
statusenumqueued, preparing, ready, or served

Moves the order’s kitchen ticket to a prep stage. This is the same transition staff make with the Preparing, Ready, and Served buttons on the Kitchen board, with the same effects: the ticket moves on the board, the customer receives the WhatsApp stage message the business has configured, and a kitchen_ticket.status_updated webhook is sent. Use it when the kitchen is run from an external system such as a kitchen display, a delivery aggregator, or your own POS.

Stages are not enforced in order; a ticket can move forward or back to any stage. Only the kitchen ticket is affected, never a bar ticket on the same order.

The order must already have a kitchen ticket, which it gets from send_to_kitchen: true at creation or from staff sending it to the kitchen. If kitchenTicketId on the order is null, this endpoint returns 404.

Response

{ "message": "Kitchen status updated", "data": { "id": "5037c85a-44c6-4c3a-bd7d-3e21e73915e1", "shortCode": "482193", "kitchenTicketId": "d2f6a1c4-8b3e-4f7a-9e1d-6c2b5a8f3e90", "kitchenTicketStatus": "preparing" }, "meta": { "notification": { "status": "sent", "customerName": "Ada Lovelace", "customerPhone": "+2348012345678", "messageId": "wamid.HBg..." } } }

data is the full order resource with kitchenTicketStatus reflecting the new stage. meta.notification reports what happened with the customer’s WhatsApp stage message; Cloove only sends one when the business has enabled it for that stage.

notification.statusMeaning
sentDelivered to WhatsApp; messageId, customerName, and customerPhone are included
skippedNothing was sent; reason says why
failedSending was attempted and did not go through; reason is whatsapp_send_failed or notification_error
notification.reasonWhen it occurs
status_unchangedThe ticket was already at the requested stage
auto_send_disabledAutomatic messages are not enabled for this stage
already_sentThe customer was already messaged for this stage on this ticket
missing_saleThe ticket is not linked to a recorded order
sale_not_foundThe linked order could not be loaded
missing_customer_phoneThe order has no customer WhatsApp or phone number
whatsapp_not_connectedThe business has no active WhatsApp number
empty_messageThe configured stage message is blank
notification_errorAn unexpected error occurred while sending

A skipped or failed notification never fails the request. The stage is saved before the message is attempted, so 200 OK always means the ticket is at the requested stage.

Idempotency

Repeating a request with the same Idempotency-Key returns the original response and does not send another WhatsApp message or webhook. Repeating the same status without a key is also safe: it returns 200 OK with notification.reason set to status_unchanged and emits no webhook.

A 404 with the message This order has no associated kitchen ticket means the order was never sent to the kitchen. Create orders with send_to_kitchen: true when an external system will drive their prep status.

Refresh an automated payment

POST /v1/orders/5037c85a-44c6-4c3a-bd7d-3e21e73915e1/requery

Use this endpoint for an order with isAutomated: true when your application needs to refresh its virtual account payment state. The response returns the updated order resource.

Do not poll continuously. Prefer webhooks for normal payment updates and requery only after a customer reports payment or when recovering from a missed event.

Generate a receipt

POST /v1/orders/5037c85a-44c6-4c3a-bd7d-3e21e73915e1/receipt
{ "message": "Order receipt generated", "data": { "url": "https://storage.example.com/receipts/signed-receipt.pdf" } }

Receipt URLs can be signed and time-limited. Store the order ID and generate another URL when a previous link expires.

Delete an order

DELETE /v1/orders/5037c85a-44c6-4c3a-bd7d-3e21e73915e1

Deletion soft-deletes the order, restores tracked variant inventory, and removes its active debt record. It is intended for incorrectly recorded orders.

Orders with automated deposits cannot be deleted and return 409 Conflict:

{ "error": "order_not_deletable", "message": "Orders with automated deposits (like bank transfers) cannot be deleted." }

Use cancellation for a valid order that will not be fulfilled. Use deletion only when the record itself should not remain in sales reporting.

Errors

HTTP statusErrorWhen it occurs
400request_failedA domain rule prevents the operation
400order_not_recordedOrder creation fails
401unauthorizedAPI key is missing or invalid
403forbiddenAPI key lacks the required scope
404not_foundOrder is missing, deleted, or belongs to another business
409order_not_deletableAn automated-deposit order cannot be deleted
409request_failedThe order already has an active kitchen ticket
422validation_errorA request field or list filter is invalid
429rate_limit_exceededDeveloper API rate limit is exceeded

See API conventions for the shared error envelope, retry rules, pagination, and idempotency behavior.

Creating an order emits an order.created webhook. Status and payment changes may emit additional order or payment events. Sending an order to the kitchen emits kitchen_ticket.created, and kitchen stage changes emit kitchen_ticket.status_updated. Verify webhook signatures and process event IDs idempotently.

Last updated on