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 noteYou 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.
| Status | Meaning |
|---|---|
new | Delivery created, awaiting courier assignment |
placed | Courier dispatched |
arrived | Courier at the pickup location |
enroute | Courier picked up the order, heading to customer |
delivered | Order delivered |
cancelled | Delivery 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, omitpickup_datetime, and providedrop_off_datetimeinstead.
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/jsonSame 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:
- Open the KitchenHub dashboard.
- Go to any test location and open the Delivery section.
- Create a new delivery and fill out the delivery details.
- Enable the Test Delivery toggle. This ensures your delivery actions are simulated without interacting with real delivery providers.
- 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/jsonSame 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
| Property | Type | Nullable | Description |
|---|---|---|---|
id | integer | No | A unique identifier for the delivery |
order_id | string | No | Your order identifier |
store_id | string | No | A unique identifier for the store where the order is processed |
scheduled_order | boolean | Yes | Indicates whether the order is scheduled for a future time or is immediate |
delivery_order_id | string | Yes | A unique identifier from the delivery provider |
cancellation_reason | string | Yes | The reason for cancellation if the delivery is canceled |
delivery_type | string | No | restaurant — self-delivery using services such as DoorDash Drive or DeliveryLogic; provider — delivery by the provider itself (GrubHub, Uber Eats, etc.) |
pickup_info | object | No | Pulled from the location settings. See PickupInfo |
customer_info | object | No | Information about the customer. See CustomerInfo |
drop_off_info | object | No | Drop-off location and time details. See DropOffInfo |
charges | object | No | Breakdown of charges. See Charges |
status | string | No | Current delivery status. See Delivery lifecycle |
created_at | date-time | No | Timestamp when the delivery was created (UTC) |
delivery_provider_id | string | No | The delivery service handling the order (doordash_drive, delivery_logic, cartwheel) |
courier_info | object | Yes | Courier details once assigned. See CourierInfo |
delivery_at | date-time | Yes | Timestamp when the order was delivered (UTC) |
cancelled_at | date-time | Yes | Timestamp when the delivery was cancelled (UTC) |
accepted_at | date-time | Yes | Timestamp when the delivery was accepted (UTC) |
is_mock_delivery | boolean | No | Indicates whether this is a test delivery |
PickupInfo Object
| Property | Type | Nullable | Description |
|---|---|---|---|
store_name | string | No | Populated from the store name |
store_address | string | No | Populated from the store address |
pickup_notes | string | Yes | Populated from the store settings |
estimated_pickup_at | date-time | Yes | Estimated time when the order will be ready for pickup |
CustomerInfo Object
| Property | Type | Nullable | Description |
|---|---|---|---|
first_name | string | No | The customer's first name |
last_name | string | No | The customer's last name |
phone_number | string | No | The customer's phone number (E.164 format) |
email | string | Yes | The customer's email address |
DropOffInfo Object
| Property | Type | Nullable | Description |
|---|---|---|---|
drop_off_address | string | No | The address where the order will be delivered |
drop_off_datetime | date-time | Yes | Scheduled drop-off time. Only for scheduled deliveries |
drop_off_notes | string | Yes | Special instructions for the courier |
estimated_dropoff_at | date-time | Yes | Estimated delivery time |
Charges Object
| Property | Type | Nullable | Description |
|---|---|---|---|
total | decimal | No | The total amount to be paid |
delivery_fee | decimal | Yes | Fee charged for the delivery service |
delivery_tips | decimal | Yes | Tip for the courier |
subtotal | decimal | Yes | Subtotal amount |
CourierInfo Object
| Property | Type | Nullable | Description |
|---|---|---|---|
id | string/int | Yes | Unique identifier for the courier |
name | string | Yes | The courier's name |
phone_number | string | Yes | The courier's contact number |
phone_code | string | Yes | International dialing code |
photo_url | string | Yes | URL to the courier's photo |
vehicle_type | string | Yes | Type 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.
| Method | Path | Description |
|---|---|---|
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_id | Quote support | Description |
|---|---|---|
delivery_logic | — | Direct dispatch |
doordash_drive | Yes | DoorDash Drive |
cartwheel | — | Cartwheel |
Delivery services without quote support still appear in /v2/quotes/ results — the delivery is created at accept time, not at quote time.
