API Reference

The Orders API enables your system to receive, manage, and track customer orders coming from third-party marketplaces integrated with Kitchenhub. It supports both webhook-based and polling-based integration models.

๐Ÿงญ How It Works (Overview)



At a high level:

  1. Orders are received from marketplaces (e.g., DoorDash, Uber Eats, etc.).
  2. Kitchenhub processes and normalizes the data.
  3. Your system receives the order via a webhook notification or retrieves it via polling.
  4. You can respond to the order (accept, cancel, etc.) using the API.

๐Ÿš€ Webhook Notifications (Recommended)

Kitchenhub sends real-time notifications for new orders to a dedicated webhook endpoint.

How It Works:

  • Configuration: In your Kitchenhub integration, configure your webhook URL to point to the endpoint on your server that will handle order notifications. The correct endpoint is:
    POST /webhooks/order
    
  • Delivery: When a new order is received, Kitchenhub sends a POST request to /webhooks/order with the order payload.
  • Acknowledgment: Your server must respond with an HTTP 200 (OK) status to confirm receipt.
  • Processing: Once acknowledged, the order can be managed via subsequent API calls.

๐Ÿ’ก

Using webhooks ensures minimal delay for real-time order processing.


๐Ÿ“ฆ Order Payload Example

Below is an example payload that aligns with the specification:

{ "order_id": "123456", "status": "new", "store_id": "store_001", "scheduled_time": null, "customer": { "name": "John Doe", "phone": "+1234567890" }, "items": [ { "item_id": "burger_001", "name": "Cheeseburger", "quantity": 2, "modifiers": [ { "name": "Extra cheese" } ] } ], "total_price": 19.99, "delivery_type": "pickup", "marketplace": "Doordash" }

Notes:

  • scheduled_time is a date-time string in ISO 8601 format or null.
  • delivery_type accepts values like pickup or delivery.
  • All fields are required as per the OpenAPI specification.

๐Ÿ” Order Status Lifecycle

The standard order statuses are as follows:

StatusDescription
newThe order has been created and is awaiting processing.
acceptedThe order has been accepted; the preparation time has been set.
completedThe order has been prepared and handed off to the customer or courier.
cancelledThe order has been cancelled (by the customer, system, or store).

๐Ÿ•’

Orders should be responded to within the time window defined by the provider (typically up to 5-15 minutes).


๐Ÿ“ฅ Managing Orders

Accept Order

  • Endpoint:

    POST /orders/{order_id}/accept
    
  • Description: Accepts the order and sets the preparation time.

  • Request Payload:

    { "prep_time": 15 }

    where prep_time is the preparation time in minutes.

  • Response: HTTP 200 OK upon successful acceptance.


Cancel Order

  • Endpoint:
    POST /orders/{order_id}/cancel
    
  • Description: Cancels the order.
  • Request Payload:
    No payload is required (if needed, send an empty JSON object {}).
  • Response: HTTP 200 OK upon successful cancellation.

Retrieve Orders (Polling)

If your system does not support webhooks, you can periodically poll for new orders:

  • Endpoint:
    GET /orders
    
  • Description: Retrieves a list of orders in JSON format (an array of order objects).

๐Ÿ”„

Polling is an alternative method to retrieve orders when webhook integration is not available.


๐Ÿงช Testing

Kitchenhub provides a staging environment where you can validate and test your integration. In the staging environment you can:

  • Webhook Testing:
    Validate webhook delivery by sending test orders to the /webhooks/order endpoint.

  • Order Management Testing:
    Test your order acceptance and cancellation logic using the respective endpoints (POST /orders/{order_id}/accept and POST /orders/{order_id}/cancel).

  • Order Polling:
    Monitor order status updates end-to-end by periodically retrieving orders via GET /orders.

  • Mock Order Endpoint:
    In addition to the above, Kitchenhub provides a mock order endpoint that allows you to simulate incoming orders without affecting live data. To create a test order, send a POST request to the mock order endpoint in the staging environment:

    POST /orders/mock
    

    Example Test Order Payload:

    { "order_id": "test123", "status": "new", "store_id": "store_test", "scheduled_time": null, "customer": { "name": "Test User", "phone": "+1000000000" }, "items": [ { "item_id": "test_item_001", "name": "Test Burger", "quantity": 1, "modifiers": [ { "name": "No pickles" } ] } ], "total_price": 9.99, "delivery_type": "pickup", "marketplace": "TestMarket" }

    This test order is processed like a real order, allowing you to verify the end-to-end flow of order creation, processing, and management in your staging environment.



โ“ FAQ

Why do some orders arrive with a status other than new?

Orders may already be in accepted, completed, or cancelled status if they were processed on another device (e.g., a POS system or tablet connected to the same provider account). The webhook will always reflect the current state of the order.

Why I cannot accept an order?

If the provider is already connected to another POS, aggregator, or Kitchenhub restaurant, your integration can be in not main account (read only) mode. You wonโ€™t be able to accept or cancel orders in that case.

Why canโ€™t I cancel an order after accepting it?

Once an order has been accepted, it cannot be cancelled through our API. Some providers (like DoorDash) enforce this rule strictly.

Why is DoorDash cancelling my accepted orders?

If the order was accepted on a tablet and not acknowledged by your integration, DoorDash may cancel it via API. To prevent this, restaurants must disable or disconnect their DoorDash tablet when using Kitchenhub.

Why was the order cancelled before we accepted it?

If an order is not accepted within the providerโ€™s timeout window, it will be automatically cancelled by the provider.

What happens if I ignore several orders?

If your integration repeatedly fails to accept orders, the provider may temporarily disable the store. In that case, it will need to be manually reactivated. We strongly recommend handling all orders promptly.

Why is customer contact info or address missing?

Some providers intentionally hide customer data (phone, email, or address) for privacy reasons. This is common with certain delivery partners.

Can I filter which orders are sent to my webhook?

Yes. You can configure webhook delivery filters by status, delivery type, or other attributes. Contact us to set this up.

Why doesnโ€™t the total order price match the sum of items?

We do not calculate totals โ€” all pricing data comes directly from the provider. Minor differences may exist due to promotions, taxes, or rounding logic used by the marketplace.

Can item modifiers be nested?

Yes. Some providers support nested options (e.g., sauce inside a combo meal). Kitchenhub reflects these structures in the modifiers field if available.

Why is the price calculation slightly different for each provider?

Each marketplace has its own tax, discount, fee, and rounding rules. Kitchenhub displays pricing exactly as received โ€” we do not normalize amounts.

Do all providers support scheduled orders?

No. Some providers do not send scheduled orders in advance. These orders are often delivered as ASAP orders shortly before prep time.

What is automatic order release for DoorDash?

We support an automatic order release mode for DoorDash to streamline high-volume operations. Contact our support team to learn how to enable it.

Why do some orders arrive with delay?

While most orders arrive in real time, some may be delayed by a few minutes depending on the providerโ€™s infrastructure and delivery status updates.

What happens if item mapping fails?

If your menu is mapped with item IDs and we canโ€™t match an incoming item, weโ€™ll return null for partner_id. Itโ€™s up to you to decide how to handle such unmatched items (e.g., notify staff, flag the order, etc.).

When do driver details appear?

Driver details (name, phone, vehicle info) may not be available at the time of order creation. They are usually added later in a follow-up webhook update.


๐Ÿ”  Order Model

The Order object is fully detailed below, and weโ€™ve also included a sample instance here. This Order object will contain an ID field that youโ€™ll need later to confirm the order, so please keep track of it. All incoming orders will have a status of NEW.

The Order object always contains these top-level properties.

PropertyTypeNullableDescription
orderObjectNoBasic order info
providerObjectNoProvider info
storeObjectNoStore info
customerObjectNoCustomer info
itemsArray[Item]NoList of order items
deliveryObjectNoDelivery info
paymentObjectNoPayment info
chargesObjectNoCharges info
timestampsObjectNoDates and times info

order object

PropertyTypeNullableDescription
idIntegerNoID of order on KitchenHub side
external_idStringNoID of order on the provider side
numberStringNoHuman readable order number
daily_numberIntegerNoSerial order number for the current day
typeStringNoThe type of order, can be either pickup, delivery or dinein
statusStringNoThe status of order can be either new, accepted, completed or canceled
notesStringYesCustomer notes. null if notes not set
paidBooleanNoWhether the order paid or not
asapBooleanNoWhether order ASAP or for future
add_disposable_itemsBooleanYesWhether to add disposable items or not
prep_time_minutesIntegerYesPreparation time selected while accepting an order. null if the order was accepted using the provider's app

provider object

PropertyTypeNullableDescription
idStringNoProvider identifier.
nameStringNoHuman readable provider name
merchant_idStringYesMerchant ID on the provider side.
store_idStringYesStore ID on a provider side.

location object

PropertyTypeNullableDescription
idStringNoLocation ID on KitchenHub side
partner_location_idStringYesLocation ID on partner side. Can be set via API while creating a location or through KitchenHub web dashboard
nameStringNoLocation name on KitchenHub side
streetStringYesLocation street
cityStringYesLocation city
stateStringYesLocation state
zipcodeStringYesLocation zipcode
latFloatYesLocation latitude coordinate
lonFloatYesLocation longitude coordinate

store object

PropertyTypeNullableDescription
idStringNoStore ID on KitchenHub side
partner_store_idStringYesStore ID on partner side. Can be set via API while creating store or through KitchenHub web dashboard
nameStringNoStore name on KitchenHub side

customer object

PropertyTypeNullableDescription
nameStringNoCustomer name
emailStringYesCustomer email. null if email is not available on a provider side
phone_numberStringYesCustomer phone number without country code. null if it is not available on a provider side
phone_codeStringYesAdditional phone code if a provider uses phone masking. null if it is not available on a provider side

item object

PropertyTypeNullableDescription
idStringYesID of menu item on provider side
partner_idStringYesID of menu item on your side
nameStringNoName of menu item on provider side
quantityIntegerNoQuantity of order item
priceStringNoPrice of order item (includes options prices)
instructionsStringYesCustomer notes for order item
optionsArray[Option]NoList of options for order item

option object

PropertyTypeNullableDescription
idStringYesID of menu option on provider side. null if item ID is not available in order data
partner_idStringYesID of option on your side
nameStringNoName of menu option on provider side
quantityIntegerNoQuantity of item option
priceStringNoPrice of item option
modifier_nameStringYesName of menu modifier on provider side, for example: Choose size. null if modifier name is not available in order data

delivery object

PropertyTypeNullableDescription
typeStringNoIndicates who is responsible for delivery - restaurant or provider
statusStringYesCurrent delivery status. Can be either started, arriving, or delivered. null if order type is pickup or delivery by provider.
notesStringYesDelivery notes. null if delivery notes are not set
addressObjectNoDelivery address info
courierObjectNoCourier info

address object

PropertyTypeNullableDescription
countryStringYesDelivery country. null if order type is pickup
streetStringYesDelivery street. null if order type is pickup
cityStringYesDelivery city. null if order type is pickup
stateStringYesDelivery state. null if order type is pickup
zipcodeStringYesDelivery zip code. null if order type is pickup
unit_numberStringYesAdditional unit number. null if order type is pickup
latitudeFloatYesDelivery latitude. null if order type is pickup or data not available
longitudeFloatYesDelivery longitude. null if order type is pickup or data not available

courier object

PropertyTypeNullableDescription
nameStringYesCourier name. null if order type is pickup or courier is not assigned yet
photo_urlStringYesCourier photo. null if order type is pickup, courier is not assigned yet or this data is not available on the provider side
phone_numberStringYesCourier phone number. null if order type is pickup, courier is not assigned yet or this data is not available on provider side
statusStringYesDispatching status. Can be either pending or assigned. null if order type is pickup

payment object

PropertyTypeNullableDescription
methodStringNoName of payment method used to pay for an order

charges object

PropertyTypeNullableDescription
subtotalStringNoThe amount before any taxes, tips, discounts, or fees.
taxStringNoThe amount of tax applied to the subtotal based on the tax rate.
tipsStringYesThe amount of gratuity or tips given to a courier.
restaurant_tip_amountStringYesThe amount of tips that go directly to the restaurant.
discountStringYesThe amount deducted from the subtotal due to promotions or offers.
totalStringNoThe final amount after applying taxes, tips, discounts, and fees.
commissionStringYesThe fixed fee charged by the platform or service provider (not for all providers).
processing_feeStringYesThe fee charged for processing payments, usually based on transaction amount.
adjustmentStringYesAny modifications to the order total, such as refunds or corrections.
tax_payoutStringYesThe amount of tax that needs to be paid to tax authorities by restaurant. Could be 0, if tax collected by a marketplace.
delivery_feeStringYesThe fee charged for delivering the order to the customer.
service_feeStringYesA fee for using the platform or service to the customer.
payoutStringYesThe actual amount the restaurant receives after all fees and commissions.
other_feeObjectYesAny additional fees not categorized above, such as packaging or special service fees.

timestamps object

All timestamps are in UTC formatted according to ISO8601 standard.

PropertyTypeNullableDescription
placed_atStringNodate and time when the order was created on the provider side
created_atStringNodate and time when the order was created on the KitchenHub side
accepted_atStringYesdate and time when the order was accepted on the KitchenHub side
cancelled_atStringYesdate and time when the order was canceled on the KitchenHub side
cancelled_byStringYes
completed_atStringYesdate and time when order was canceled on KitchenHub side
pickup_atStringNodate and time when the order should be ready for pickup by the customer or courier
delivery_atStringYesdate and time when order should be delivered
scheduled_forStringYesdate and time when order should be delivered if order is for future (asap==false)

๐Ÿ“ Order Example

{ "order": { "id": 5501505922269184, "external_id": "4ae18552-60a8-11ee-8553-abf2eec4f777", "number": "201523913381757", "daily_number": 15, "type": "delivery", "status": "completed", "notes": "Contactless delivery, Please don't ring the doorbell.", "paid": true, "asap": false, "add_disposable_items": true, "prep_time_minutes": null, "data": { } }, "provider": { "id": "grubhub", "name": "GrubHub", "merchant_id": "2626147", "store_id": "c212a250-3b69-45ab-9184-03fb2072x7xx" }, "store": { "id": "c212a250-3b69-45ab-9184-03fb2072x7xx", "name": "The Spot Asian Food Hall", "partner_store_id": null }, "customer": { "name": "Ari S", "email": null, "phone_number": "3012136310", "phone_code": null }, "items": [ { "id": "6035887902", "name": "2 Proteins", "quantity": 1, "price": "22.25", "instructions": null, "options": [ { "id": "6035887894", "name": "Sushi Rice", "quantity": 1, "price": "0.0", "modifier_name": null }, { "id": "6035887878", "name": "Mango", "quantity": 1, "price": "0.0", "modifier_name": null }, { "id": "6035887903", "name": "Seaweed Salad", "quantity": 1, "price": "0.0", "modifier_name": null } ] }, { "id": "6027293408", "name": "Magic Roll", "quantity": 1, "price": "13.45", "instructions": null, "options": [ ] }, { "id": "6027293408", "name": "Magic Roll", "quantity": 1, "price": "13.45", "instructions": null, "options": [ ] }, { "id": "6035887902", "name": "2 Proteins", "quantity": 1, "price": "16.75", "instructions": null, "options": [ { "id": "6035887894", "name": "Sushi Rice", "quantity": 1, "price": "0.0", "modifier_name": null }, { "id": "6035887867", "name": "Avocado Egg Salad", "quantity": 1, "price": "0.0", "modifier_name": null }, { "id": "6035887922", "name": "Organic Tofu", "quantity": 1, "price": "0.0", "modifier_name": null }, { "id": "6035887912", "name": "Sesame Seed", "quantity": 1, "price": "0.0", "modifier_name": null } ] } ], "delivery": { "type": "provider", "address": { "country": "US", "street": "1234 Bettstrail Way", "city": "Rockville", "state": "MD", "zipcode": "20854", "unit_number": null, "latitude": 39.07294, "longitude": -77.17325 }, "courier": { "name": "Donald V", "photo_url": "https://s3.amazonaws.com/d5eb54503152.png", "phone_number": "2407930399", "status": "assigned" }, "status": null, "notes": null }, "payment": { "method": "CREDIT CARD" }, "charges": { "discount": "0.0", "commission": null, "processing_fee": null, "adjustment": null, "tax_payout": "0.0", "restaurant_tip_amount": "0.0", "delivery_fee": null, "subtotal": "66.84", "total": "72.44", "tips": "0.0", "service_fee": null, "tax": "5.6", "payout": null, "other_fee": {} }, "timestamps": { "placed_at": "2023-10-01T22:20:26Z", "created_at": "2023-10-01T22:21:44Z", "pickup_at": "2023-10-01T23:45:00Z", "accepted_at": "2023-10-01T23:14:44Z", "cancelled_at": null, "cancelled_by": null, "completed_at": "2023-10-01T23:30:45Z", "delivery_at": "2023-10-01T23:45:00Z", "scheduled_for": "2023-10-01T23:45:00Z" } }