🛵 Deliveries API


The Delivery API is designed to allow technology partners seamlessly integrate third-party delivery providers into their solution.

🌪️ Problem definition

Tech services that work with restaurants, such as POS systems or online ordering solutions, often face challenges when providing delivery options. Without a standardized delivery integration, they have to either leave delivery logistics entirely up to the restaurants or integrate separately with various delivery providers, which is time-consuming and complex. Managing different APIs, workflows, and tracking systems for each delivery provider creates operational inefficiencies and makes scaling difficult. This lack of integration can result in poor customer experience, delayed deliveries, and limited delivery tracking visibility for restaurants and their customers.

📦 Delivery API Solution

We offer a unified, easy-to-integrate API that connects tech platforms to multiple third-party delivery providers, such as DoorDash Drive, DeliverLogic, and others in the future. This allows you to offer seamless delivery management for your customers, without having to build complex integrations with each delivery provider individually.
This API abstracts the complexity of managing deliveries across multiple platforms and offers a plug-and-play solution for tech services.


🪧 Prerequisite to Use the Delivery API

Create an Authorization Token

If you don't have access to the KitchenHub API, you must first obtain API keys. Using these keys, you can generate an authorization token to authenticate your requests. This token ensures secure communication between your system and the API by validating each request.
For more information, refer to the Auth API.

Connect a Restaurant

Before managing deliveries, you must first set up your restaurant's locations and stores. This setup allows the API to manage deliveries for the registered stores.
For detailed instructions, refer to the KitchenHub API Locations documentation.

📘

Please note

You need to save store ID on your side to order for further delivery requests for this store.

The following properties will be used in the delivery object from the store and location settings:

  • **Store Name*he name of the store.
  • Street: Restaurant's street address.
  • City: Restaurant's city.
  • State: Restaurant's state.
  • Zipcode: Restaurant's postal code.
  • location_timezone: The timezone of the location (e.g., America/New_York).
  • pickup_notes: Notes about the location's pickup instructions.

Connect Delivery Providers to the Store

In addition to setting up location and store, you must connect them with a delivery provider. For detailed instructions on how to connect a store to a delivery provider, visit the Providers API documentation.

Setup Webhook for Delivery Notifications (Optional)

To receive real-time updates on your deliveries, you can configure webhooks of type Delivery. These webhooks will notify you instantly of key events in the delivery process, including:

  • QuoteCreated: Notification when a delivery quote is generated.
  • DeliveryCreated: Notification when a delivery order has been successfully created.
  • DeliveryUpdated: Notification when the status of a delivery changes.

Setting up webhooks ensures timely updates and enhances tracking capabilities for your deliveries. For a step-by-step guide on configuring webhooks and details on the different event types, refer to the webhook section in the documentation.


Delivery lifecycle

stateDiagram-v2
    [*] --> new : delivery created
    new --> placed : courier dispatched
    placed --> arrived : courier at pickup
    placed --> enroute : courier picked up order
    arrived --> enroute : courier picked up order
    enroute --> delivered : delivered to customer

    new --> cancelled
    placed --> cancelled
    arrived --> cancelled
    enroute --> cancelled

    delivered --> [*]
    cancelled --> [*]
❗️

DoorDash Drive deliveries cannot be canceled after Dasher is Assigned.

StatusMeaning
newDelivery created, awaiting courier assignment
placedCourier dispatched
arrivedCourier at the pickup location
enrouteCourier picked up the order, heading to customer
deliveredOrder delivered
cancelledDelivery cancelled (terminal)

Flow A — Direct delivery

Use this when you want to dispatch immediately with a specific delivery service.

sequenceDiagram
    participant You
    participant API as KitchenHub API

    You->>API: POST /v2/deliveries/
    API-->>You: 201 { delivery_id, status: "new", ... }

    Note over You,API: Poll or receive webhooks for status changes

    You->>API: GET /v2/deliveries/{delivery_id}/
    API-->>You: 200 { status: "enroute", courier_info: {...}, tracking_url }

Request:

POST /v2/deliveries/
Authorization: Bearer <token>
Content-Type: application/json
{
  "order_id": "order-123",
  "store_id": "your-store-id",
  "scheduled_order": false,
  "delivery_provider_id": "doordash_drive",
  "pickup_info": {
    "pickup_datetime": "2025-05-05T14:00:00"
  },
  "customer_info": {
    "first_name": "Jane",
    "last_name": "Doe",
    "phone_number": "+12025550100",
    "email": "[email protected]"
  },
  "drop_off_info": {
    "drop_off_address": "123 Main St, New York, NY 10001",
    "drop_off_notes": "Leave at door"
  },
  "charges": {
    "total": "45.00",
    "subtotal": "36.01",
    "delivery_fee": "5.99",
    "delivery_tips": "3.00"
  },
  "items": [
    { "name": "Burger", "quantity": 2, "price": "12.00", "is_alcohol": false }
  ]
}

Scheduled orders: set "scheduled_order": true, omit pickup_datetime, and provide drop_off_datetime instead.

Response 201:

{
  "delivery_id": "550e8400-e29b-41d4-a716-446655440000",
  "order_id": "order-123",
  "store_id": "your-store-id",
  "status": "new",
  "delivery_provider_id": "doordash_drive",
  "delivery_order_id": "dsd-abc123",
  "tracking_url": "https://doordash.com/track/abc123",
  "scheduled_order": false,
  "pickup_info": {
    "store_name": "My Burger",
    "store_address": "456 Restaurant Ave, New York, NY",
    "pickup_datetime": "2025-05-05T14:00:00.000000Z",
    "estimated_pickup_at": "2025-05-05T14:10:00.000000Z"
  },
  "drop_off_info": {
    "drop_off_address": "123 Main St, New York, NY 10001",
    "drop_off_notes": "Leave at door",
    "estimated_dropoff_at": "2025-05-05T14:35:00.000000Z"
  },
  "customer_info": {
    "first_name": "Jane",
    "last_name": "Doe",
    "phone_number": "+12025550100",
    "email": "[email protected]"
  },
  "charges": {
    "total": "45.00",
    "subtotal": "36.01",
    "delivery_fee": "5.99",
    "delivery_tips": "3.00"
  },
  "courier_info": null,
  "created_at": "2025-05-05T13:55:00.000000Z",
  "accepted_at": null,
  "delivery_at": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "is_mock_delivery": false
}

Flow B — Quote & accept

Use this to compare prices across all your connected delivery services before dispatching.

sequenceDiagram
    participant You
    participant API as KitchenHub API

    You->>API: POST /v2/quotes/
    API-->>You: 201 { delivery_id, expired_at, options: [{delivery_service, fee}, ...] }

    Note over You,API: You have ~5 minutes to pick an option

    You->>API: POST /v2/quotes/{delivery_id}/accept/
    API-->>You: 200 { delivery object }

Step 1 — Request quotes:

POST /v2/quotes/
Authorization: Bearer <token>
Content-Type: application/json

Same body as POST /v2/deliveries/ but without delivery_provider_id.

Response 201:

{
  "delivery_id": "550e8400-e29b-41d4-a716-446655440000",
  "expired_at": "2025-05-05T14:00:00.000000Z",
  "options": [
    {
      "delivery_provider_id": "delivery_logic",
      "delivery_order_id": null,
      "delivery_fee": "4.50",
      "error_description": null
    },
    {
      "delivery_provider_id": "doordash_drive",
      "delivery_order_id": "dsd-abc123",
      "delivery_fee": "5.99",
      "error_description": null
    },
    {
      "delivery_provider_id": "cartwheel",
      "delivery_order_id": null,
      "delivery_fee": null,
      "error_description": "Delivery service unavailable in this area"
    }
  ]
}

Options with a non-null error_description cannot be accepted. Quotes expire at expired_at.

Step 2 — Accept a quote:

POST /v2/quotes/{delivery_id}/accept/
Authorization: Bearer <token>
Content-Type: application/json
{
  "delivery_provider_id": "delivery_logic",
  "tip": 3.00
}

Returns the full delivery object (same shape as the POST /v2/deliveries/ response).


Cancel a delivery

PUT /v2/deliveries/{delivery_id}/status/
Authorization: Bearer <token>
Content-Type: application/json
{
  "status": "cancelled"
}

Returns the updated delivery object. A delivery can be cancelled from any non-terminal status (new, placed, arrived, enroute).

❗️

DoorDash Drive deliveries cannot be canceled after Dasher is Assigned.


Testing

To test the Delivery API for KitchenHub, there are two options: through the dashboard or the API. Below is the documentation for both approaches, including how to create, update, and manage deliveries in test mode.

Test Delivery via Dashboard

Create Delivery in Dashboard:

  1. Open the KitchenHub dashboard.
  2. Go to any test location and open the Delivery section.
  3. Create a new delivery and fill out the delivery details.
  4. Enable the Test Delivery toggle. This ensures your delivery actions are simulated without interacting with real delivery providers.
  5. Submit the form.

Change Status in Dashboard:

In test mode, you can directly update the delivery status (e.g., "Enroute," "Delivered") within the dashboard interface.

Without the Test Delivery toggle enabled, the system will send the delivery request to actual providers like DoorDash or UberEats.

Test Delivery via API

Mock deliveries let you test the full delivery flow without involving a real courier. Status transitions are triggered manually, so you can simulate any scenario in your dev/staging environment.

Create a mock delivery:

POST /v2/mock/deliveries/
Authorization: Bearer <token>
Content-Type: application/json

Same request body as POST /v2/deliveries/. Returns the same delivery object with "is_mock_delivery": true.

Manually advance the status:

Use the delivery_order_id from the create response.

PUT /v2/mock/deliveries/{delivery_order_id}/status/
Authorization: Bearer <token>
Content-Type: application/json
{
  "status": "enroute"
}

You can set any valid status (placed, arrived, enroute, delivered, cancelled) to walk through the lifecycle at your own pace. Returns the updated delivery object.


Delivery Model

The Delivery object is described below, with a sample instance included for clarity. The id field is crucial for tracking and updating the delivery status. All newly created delivery orders start with a status of new.

Delivery Object

PropertyTypeNullableDescription
idintegerNoA unique identifier for the delivery
order_idstringNoYour order identifier
store_idstringNoA unique identifier for the store where the order is processed
scheduled_orderbooleanYesIndicates whether the order is scheduled for a future time or is immediate
delivery_order_idstringYesA unique identifier from the delivery provider
cancellation_reasonstringYesThe reason for cancellation if the delivery is canceled
delivery_typestringNorestaurant — self-delivery using services such as DoorDash Drive or DeliveryLogic; provider — delivery by the provider itself (GrubHub, Uber Eats, etc.)
pickup_infoobjectNoPulled from the location settings. See PickupInfo
customer_infoobjectNoInformation about the customer. See CustomerInfo
drop_off_infoobjectNoDrop-off location and time details. See DropOffInfo
chargesobjectNoBreakdown of charges. See Charges
statusstringNoCurrent delivery status. See Delivery lifecycle
created_atdate-timeNoTimestamp when the delivery was created (UTC)
delivery_provider_idstringNoThe delivery service handling the order (doordash_drive, delivery_logic, cartwheel)
courier_infoobjectYesCourier details once assigned. See CourierInfo
delivery_atdate-timeYesTimestamp when the order was delivered (UTC)
cancelled_atdate-timeYesTimestamp when the delivery was cancelled (UTC)
accepted_atdate-timeYesTimestamp when the delivery was accepted (UTC)
is_mock_deliverybooleanNoIndicates whether this is a test delivery

PickupInfo Object

PropertyTypeNullableDescription
store_namestringNoPopulated from the store name
store_addressstringNoPopulated from the store address
pickup_notesstringYesPopulated from the store settings
estimated_pickup_atdate-timeYesEstimated time when the order will be ready for pickup

CustomerInfo Object

PropertyTypeNullableDescription
first_namestringNoThe customer's first name
last_namestringNoThe customer's last name
phone_numberstringNoThe customer's phone number (E.164 format)
emailstringYesThe customer's email address

DropOffInfo Object

PropertyTypeNullableDescription
drop_off_addressstringNoThe address where the order will be delivered
drop_off_datetimedate-timeYesScheduled drop-off time. Only for scheduled deliveries
drop_off_notesstringYesSpecial instructions for the courier
estimated_dropoff_atdate-timeYesEstimated delivery time

Charges Object

PropertyTypeNullableDescription
totaldecimalNoThe total amount to be paid
delivery_feedecimalYesFee charged for the delivery service
delivery_tipsdecimalYesTip for the courier
subtotaldecimalYesSubtotal amount

CourierInfo Object

PropertyTypeNullableDescription
idstring/intYesUnique identifier for the courier
namestringYesThe courier's name
phone_numberstringYesThe courier's contact number
phone_codestringYesInternational dialing code
photo_urlstringYesURL to the courier's photo
vehicle_typestringYesType of vehicle used for the delivery

Delivery Object Example

{
  "id": 467338,
  "order_id": "222172845",
  "store_id": "5d84d149-342f-4202-a614-05ae080a2d7a",
  "scheduled_order": false,
  "delivery_order_id": "183a2ff75b944a5d83b32125f0f54bb6",
  "cancellation_reason": null,
  "delivery_type": "restaurant",
  "pickup_info": {
    "pickup_datetime": "2024-09-18T22:25:19.552000Z",
    "store_name": "Auto Delivery Enabled DoorDash Drive",
    "store_address": "19 North Park Avenue, Rockville Centre, NY 11570",
    "pickup_notes": null,
    "estimated_pickup_at": null
  },
  "customer_info": {
    "first_name": "first_name test",
    "last_name": "last_name test",
    "phone_number": "+12345677877",
    "email": null
  },
  "drop_off_info": {
    "drop_off_address": "19 North Park Avenue, Rockville Centre, NY 11570, US",
    "drop_off_datetime": null,
    "drop_off_notes": "Drop-off Notes",
    "estimated_dropoff_at": null
  },
  "charges": {
    "total": "55.0",
    "delivery_fee": "9.75",
    "delivery_tips": "10",
    "subtotal": "133.0"
  },
  "status": "new",
  "created_at": "2024-10-09T07:44:21.885334Z",
  "delivery_provider_id": "doordash_drive",
  "courier_info": null,
  "delivery_at": null,
  "cancelled_at": null,
  "accepted_at": null,
  "is_mock_delivery": false
}

API Reference

For full endpoint documentation including request/response schemas, refer to the Delivery API Reference.

MethodPathDescription
POST/v2/deliveries/Create a delivery with a specific delivery service
GET/v2/deliveries/List deliveries
GET/v2/deliveries/{delivery_id}/Get a single delivery
PUT/v2/deliveries/{delivery_id}/status/Cancel a delivery
POST/v2/quotes/Request quotes from all connected delivery services
GET/v2/quotes/List quotes
POST/v2/quotes/{delivery_id}/accept/Accept a quote and dispatch
POST/v2/mock/deliveries/Create a mock delivery (testing)
PUT/v2/mock/deliveries/{delivery_order_id}/status/Manually set mock delivery status

Quotes FAQ

When is a Quote Generated?

Before creating a delivery, you can request a quote. During this process, you provide the pickup and dropoff addresses. Quotes are generated for all connected delivery services — both those that support quotes and those that don't. Delivery services that support quotes will verify the addresses, calculate the delivery fee, and return an estimated pickup and dropoff time. Delivery services that do not support quotes will still appear in the response, but with delivery_fee: null.

If the fee and time estimates are acceptable, you need to accept the quote. A quote must be accepted within 5 minutes of being requested — after that it expires and you'll need to request a new one. If you do not call POST /v2/quotes/{delivery_id}/accept/, the delivery will not be created. When calling GET /v2/deliveries/{delivery_id}/, no delivery will be returned until the accept method is invoked.


Can You Request a New Quote?

New quotes can be requested after the 5-minute expiry window. For delivery services that do not support quotes, the delivery is created immediately without a quote step.


What Happens If You Don't Accept a Quote?

The quote automatically expires after 5 minutes. Once expired, you need to initiate a new quote request.


Supported delivery services

delivery_provider_idQuote supportDescription
delivery_logicDirect dispatch
doordash_driveYesDoorDash Drive
cartwheelCartwheel

Delivery services without quote support still appear in /v2/quotes/ results — the delivery is created at accept time, not at quote time.