Skip to content

Getting started

In Dispatch, open Settings → Developers → API Access. Create a named client with:

  • quotes:create if you will show provider choices, prices, or estimates before checkout
  • orders:write to create and update orders
  • orders:read to reconcile order state
  • webhooks:read and webhooks:write if the integration manages its own webhook destination

Create a key and copy the secret immediately. It is displayed once. Store it in a secret manager—never in browser code, screenshots, logs, or committed collection files.

  • Production: https://api.firstdelivery.com/api
  • Sandbox: https://sandbox.firstdelivery.com/api

Credentials are environment-specific. A production key does not work in sandbox, and a sandbox key does not work in production.

Send the key as a Bearer token:

Terminal window
curl 'https://sandbox.firstdelivery.com/api/v3/merchants' \
--header "Authorization: Bearer $FIRST_DELIVERY_API_KEY"

New API-client keys begin with f1rst_k1_. Treat the full value as opaque.

For basic integrations, quotes are optional. You can create an order directly and let the merchant’s Dispatch Strategy choose a provider.

Request quotes first when you need to:

  • show the customer a provider choice;
  • show delivery price or pickup/drop-off estimates before checkout; or
  • preview what the configured strategy would choose right now.

The quote response is a point-in-time snapshot:

  • quotes contains successful providers after strategy bans and preference sorting;
  • all_quotes also includes successful providers that the strategy bans;
  • strategy explains the preference order and bans used for this request.

Use the real merchant ID and a realistic, geocodable drop-off address near that merchant.

Terminal window
curl 'https://sandbox.firstdelivery.com/api/v3/quotes' \
--request POST \
--header "Authorization: Bearer $FIRST_DELIVERY_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"merchant_id": "YOUR_MERCHANT_ID",
"dropoff": {
"full_address": "2166 E Norris St, Philadelphia, PA 19125",
"instructions": "Apartment 3B"
}
}'

An empty quote list usually means the address is outside providers’ service areas, the phone/address data is invalid, or the strategy has filtered every available provider. Sandbox is especially sensitive to unrealistic locations—start with pickup and drop-off addresses in the same city.

See the complete Get Provider Quotes reference.

Terminal window
curl 'https://sandbox.firstdelivery.com/api/v3/orders' \
--request POST \
--header "Authorization: Bearer $FIRST_DELIVERY_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"merchant_id": "YOUR_MERCHANT_ID",
"external_id": "order-100042",
"customer_name": "David Baldwin",
"customer_phone": "+13179385775",
"payment_type": "online",
"subtotal": 9.99,
"tax": 0.70,
"tip": 3.00,
"order_total": 13.69,
"contents": [
{ "name": "Jigowatt Nachos", "price": 9.99, "quantity": 1 }
],
"pickup": {
"instructions": "Pickup at the front counter"
},
"dropoff": {
"full_address": "2166 E Norris St, Philadelphia, PA 19125",
"instructions": "Apartment 3B"
}
}'

Save the returned First Delivery order.id; it is the canonical identifier for later API calls, webhooks, and support. Also retain _request_id when present.

See the complete Create Order reference.

Order creation supports three routing modes:

  1. Merchant default: omit quote_id and strategy_id; the merchant’s active strategy decides.
  2. Specific strategy: pass strategy_id to use that strategy instead of the merchant default. Strategy hours still apply; outside active hours the order is created but not auto-dispatched.
  3. Customer-selected provider: pass a quote id as quote_id. First attempts that quote/provider first. If the quote/provider attempt fails, routing records the error and falls back to the strategy.

Provider prices and availability change. If the customer selected a quote, fetch quotes again immediately before creating the order so they can confirm any difference. Quote rejection becomes increasingly likely after roughly five minutes, although providers have different expiration rules.

Without an automated strategy, the order appears in its status column in Dispatch for manual assignment. With an automated strategy, it appears in the external-fleet flow and moves to the courier row after assignment. Inspect auto_route.success and auto_route.errors when routing does not succeed.

  • Addresses: Prefer a geocoder-normalized dropoff.full_address. Put apartment numbers, building names, access details, and landmarks in dropoff.instructions; mixing them into the address lowers provider acceptance.
  • Phones: Use real, country-valid merchant and customer phone numbers. Providers can reject obviously fake or wrong-country numbers.
  • Scheduling: Omit both time fields for ASAP. For a scheduled order, send either pickup_time or dropoff_time, not both, as an ISO 8601 UTC timestamp at least 20 minutes ahead. Individual fleets can apply longer scheduling thresholds.
  • Money: order_total, tax, and diner_delivery_fee are primarily reporting values. subtotal affects refund eligibility and tip affects courier acceptance. Monetary values in the order request are dollars/local currency, not cents.
  • Items: Include item name, unit price, and quantity for refund eligibility. Dimensions are centimeters and weight is grams.
  • Provider options: PIN, signature, alcohol/ID checks, post-create edits, and undeliverable handling differ by fleet. Review Fleet behavior before exposing an option to customers.

In sandbox, Uber is the only network provider that simulates the full assignment-to-delivery webhook lifecycle. To receive that sequence quickly:

  • do not pass a non-Uber quote_id;
  • make sure the active strategy can select Uber; and
  • omit scheduled times.

Treat webhooks as signals that can be delayed, duplicated, or arrive out of order. Make the receiver idempotent and reconcile important state with GET /v3/orders/:id.