Ways to integrate
Three ways in, depending on who you are. All three end the same way: the cheapest quote is booked and the guest tracks the courier.
Boom Online Ordering
One switch in your BOO settings. No code, no keys: delivery orders go out for quotes as they are placed, and guests track the driver on your ordering page.
Request early access →Any ordering system
A REST API: ask for quotes, book one, cancel if you need to, and follow every delivery by webhook. One key per merchant.
Read the draft ↓Agencies
A partner key that can act for each of the restaurants you manage, with one statement a month. For agencies who resell Deliveree.
See the partner program →API v1 draft
Overview
- Base URL (draft):
https://deliveree.online/api/v1 - JSON request and response bodies; send
Content-Type: application/json. - Money is an integer number of cents (
value_cents,fee_cents,tip_cents). - Times are ISO 8601 in UTC, e.g.
2026-09-22T18:30:00Z. - ETAs come from the courier networks and are estimates, not promises.
- Values in the examples are made up. They are not prices.
The flow is always the same: quote a trip, book one of the options, then follow it by webhook (or poll it).
Authentication
Every request carries a secret key in the Authorization header:
Authorization: Bearer dlv_live_…- Live keys start
dlv_live_. Test keys startdlv_test_: use them while you build. No courier is sent and nothing is billed on a test key. - One key per merchant (per restaurant). A request made with a merchant key acts for that merchant only.
- Agencies get a partner key that can act for each of their merchants. How a request names the merchant it is for is not settled in this draft.
- Keys are secrets. Keep them on your server, never in a browser or an app bundle.
Get quotes
POST/api/v1/quotes
Ask the courier networks what a trip would cost. The response lists every option that came back and names the cheapest.
| Field | Type | Description |
|---|---|---|
pickup | object | Where the courier collects the order: name, address, phone; optional lat, lng and ready_at (when the food will be ready). |
dropoff | object | Where it goes: name, address, phone; optional lat, lng and notes for the courier. |
order | object | value_cents (the order total) and items, a list of { name, quantity }. |
tip_centsoptional | integer | Courier tip, in cents. |
curl https://deliveree.online/api/v1/quotes \
-H "Authorization: Bearer $DELIVEREE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pickup": {
"name": "Luna Pizza",
"address": "120 Main St, Tampa, FL 33602",
"phone": "+18135550142",
"ready_at": "2026-09-22T18:30:00Z"
},
"dropoff": {
"name": "Sam Rivera",
"address": "845 Bayshore Blvd, Apt 4B, Tampa, FL 33606",
"phone": "+18135550199",
"notes": "Buzz 4B"
},
"order": {
"value_cents": 4250,
"items": [
{ "name": "Large Margherita", "quantity": 1 },
{ "name": "Garlic Knots", "quantity": 2 }
]
},
"tip_cents": 500
}'| Field | Type | Description |
|---|---|---|
quote_id | string | Pass this to Book a delivery. |
expires_at | timestamp | Book before this time, or ask for a new quote. |
options | array | One entry per network that quoted: network, fee_cents, pickup_eta, dropoff_eta. |
cheapest | string | The network of the lowest-fee option. |
{
"quote_id": "qt_8Hc2kPz4",
"expires_at": "2026-09-22T18:10:00Z",
"options": [
{
"network": "doordash",
"fee_cents": 1000,
"pickup_eta": "2026-09-22T18:31:00Z",
"dropoff_eta": "2026-09-22T18:52:00Z"
},
{
"network": "uber",
"fee_cents": 1100,
"pickup_eta": "2026-09-22T18:34:00Z",
"dropoff_eta": "2026-09-22T18:50:00Z"
}
],
"cheapest": "doordash"
}Book a delivery
POST/api/v1/deliveries
Book one of the options from a quote. Deliveree picks the option by strategy.
| Field | Type | Description |
|---|---|---|
quote_id | string | From Get quotes. Must not have expired. |
external_id | string | Your own order id, echoed back on the delivery and its webhooks. |
strategy | "cheapest" | "fastest" | cheapest books the lowest fee_cents; fastest books the earliest dropoff_eta. |
tip_centsoptional | integer | Courier tip, in cents, if different from the one sent with the quote. |
curl https://deliveree.online/api/v1/deliveries \
-H "Authorization: Bearer $DELIVEREE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"quote_id": "qt_8Hc2kPz4",
"external_id": "order_1042",
"strategy": "cheapest",
"tip_cents": 500
}'| Field | Type | Description |
|---|---|---|
id | string | The delivery id. |
status | string | See Statuses. Starts at pending. |
network | string | The courier network that was booked. |
fee_cents | integer | The fee for the booked option, in cents. |
tracking_url | string | A page you can send the guest to follow the delivery. Prefer your own page? Build it from the webhooks, including courier.location. |
{
"id": "del_7Qm3Xw9A",
"status": "pending",
"network": "doordash",
"fee_cents": 1000,
"tracking_url": "https://deliveree.online/t/del_7Qm3Xw9A"
}Retrieve a delivery
GET/api/v1/deliveries/{id}
Returns the delivery object with its current status. Webhooks are the better way to follow a delivery; use this to check one on demand.
curl https://deliveree.online/api/v1/deliveries/del_7Qm3Xw9A \
-H "Authorization: Bearer $DELIVEREE_API_KEY"Cancel a delivery
POST/api/v1/deliveries/{id}/cancel
Asks the courier network to cancel. Returns the delivery object; its status is canceled if the cancel went through.
curl -X POST https://deliveree.online/api/v1/deliveries/del_7Qm3Xw9A/cancel \
-H "Authorization: Bearer $DELIVEREE_API_KEY"Statuses
Every delivery follows this flow and ends in one of three final states.
pendingcourier_assignedpicked_updeliveredcanceledfailed| Field | Type | Description |
|---|---|---|
pending | status | Booked with the network; no courier yet. |
courier_assigned | status | A courier has accepted the trip. |
picked_up | status | The courier has the order. |
delivered | final | Handed over at the drop-off. |
canceled | final | Canceled before it was delivered. |
failed | final | The network could not complete it. |
Webhooks
Give us an HTTPS URL and Deliveree will POST an event to it each time a delivery changes. Each event carries its type and the delivery it is about.
| Field | Type | Description |
|---|---|---|
delivery.created | event | A delivery was booked. |
delivery.courier_assigned | event | A courier accepted it. |
delivery.picked_up | event | The courier collected the order. |
delivery.delivered | event | It was handed over. |
delivery.canceled | event | It was canceled. |
delivery.failed | event | The network could not complete it. |
courier.location | event | The courier’s position while the delivery is under way. |
POST /webhooks/deliveree HTTP/1.1
Content-Type: application/json
Deliveree-Signature: t=1790101992,v1=<hex HMAC-SHA256 of "1790101992.<raw body>">Verify signatures
Every webhook carries a Deliveree-Signature header of the form t=<unix>,v1=<hex>. v1 is the hex HMAC-SHA256 of the string t.body (the timestamp, a full stop, then the raw request body) keyed with your signing secret.
- Compute the HMAC over the raw body, before any JSON parsing.
- Compare in constant time, and reject old timestamps so a captured request cannot be replayed.
- Answer with any
2xxonce you have stored the event.
import crypto from 'node:crypto'
const TOLERANCE_SECONDS = 300 // reject anything signed more than 5 minutes ago
/**
* rawBody: the request body exactly as received (a string), before JSON.parse.
* header: the Deliveree-Signature header, e.g. "t=1790101992,v1=5f2c…"
* secret: your webhook signing secret.
*/
export function verifyDelivereeSignature(rawBody, header, secret) {
const parts = {}
for (const piece of String(header || '').split(',')) {
const i = piece.indexOf('=')
if (i > 0) parts[piece.slice(0, i).trim()] = piece.slice(i + 1).trim()
}
const t = Number(parts.t)
if (!Number.isInteger(t) || !parts.v1) return false
if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_SECONDS) return false
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex')
const a = Buffer.from(expected, 'hex')
const b = Buffer.from(parts.v1, 'hex')
return a.length === b.length && crypto.timingSafeEqual(a, b)
}Not settled yet
This draft deliberately leaves these open. Tell us what you need.
- The error response format and error codes.
- Rate limits.
- How a partner key names the merchant a request is for.
- Whether
fee_centsincludes the dispatch fee or it is billed separately. - How long a quote lasts, and whether booking the same
external_idtwice is safe. - Whether a cancel after pickup is possible, and what the network charges for it.
- Webhook retries, and the order events arrive in.
Get access
Nothing here is self-serve yet. If you run an ordering platform and want to build against the draft, or you are a Boom Online Ordering restaurant waiting for the switch, email us and say which.