Skip to content

Quickstart

This guide takes you from an API key to a delivered sandbox order. It takes about ten minutes. You need curl, jq and a Bash or Zsh shell (on Windows, use WSL).

export DELIVR_API=https://driver-staging-api.delivr.no
export DELIVR_API_KEY=dmk_your_sandbox_key
# Every write needs a new UUID as its Idempotency-Key.
uuid() { uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid; }
  1. List your store locations. An empty list is fine; it means your key works.

    Request
    curl -s $DELIVR_API/v1/merchant/locations \
    -H "Authorization: Bearer $DELIVR_API_KEY"
    Response · 200
    { "items": [] }

    A 401 unauthenticated means the key is wrong or revoked. See Authentication.

  2. Register the store your goods are picked up from. Delivr finds it on the map from its address.

    Request
    curl -s $DELIVR_API/v1/merchant/locations \
    -H "Authorization: Bearer $DELIVR_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $(uuid)" \
    -d '{
    "externalStoreId": "oslo-sentrum",
    "name": "Oslo Sentrum",
    "street": "Akersgata 45",
    "postalCode": "0158",
    "city": "Oslo",
    "contactName": "Store staff",
    "contactPhone": "+4722000000",
    "prepMinutes": 10
    }' | jq .
    Response · 201
    {
    "id": "9b2c4f60-1d7e-4c3a-8f15-6a0e2d9b7c41",
    "externalStoreId": "oslo-sentrum",
    "name": "Oslo Sentrum",
    "street": "Akersgata 45",
    "postalCode": "0158",
    "city": "Oslo",
    "country": "NO",
    "latitude": 59.914,
    "longitude": 10.7423,
    "contactName": "Store staff",
    "contactPhone": "+4722000000",
    "prepMinutes": 10,
    "readyDeadlineMinutes": 60,
    "readySignal": "assumed",
    "pickupInstructions": ""
    }

    From now on you can name this store by your own externalStoreId. Running this step again answers 409 conflict: you already have a store with that externalStoreId, so carry on.

    Robo drivers park at a new store within a minute. Wait a minute before the next step.

  3. Ask what Delivr can offer from the store to an address, for one 4.5 kg parcel. The answer is saved to quote.json and its id to $QUOTE.

    Request
    curl -s $DELIVR_API/v1/merchant/quotes \
    -H "Authorization: Bearer $DELIVR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "pickupExternalStoreId": "oslo-sentrum",
    "delivery": {
    "address": { "street": "Karl Johans gate 22", "postalCode": "0026", "city": "Oslo" }
    },
    "parcels": [{ "count": 1, "weightKg": 4.5, "lengthCm": 40, "widthCm": 30, "heightCm": 20 }]
    }' | tee quote.json | jq .
    export QUOTE=$(jq -r .quoteId quote.json)
    Response · 200
    {
    "quoteId": "3f0e9a52-8a7e-4f8e-9a51-2c0f3c9d1b7a",
    "expiresAt": "2026-11-03T13:05:00Z",
    "timeZone": "Europe/Oslo",
    "options": [
    {
    "type": "on_demand",
    "key": "on_demand",
    "eta": { "from": "2026-11-03T13:25:00Z", "to": "2026-11-03T13:55:00Z" },
    "price": { "amountMinor": 14900, "vatMinor": 2980, "exVatMinor": 11920, "currency": "NOK" },
    "supply": {
    "nearbyDrivers": 2,
    "pickupEta": { "from": "2026-11-03T13:02:00Z", "to": "2026-11-03T13:06:00Z" }
    }
    },
    {
    "type": "window",
    "key": "window:2026-11-03T17:00:00+01:00/2026-11-03T19:00:00+01:00",
    "window": { "start": "2026-11-03T16:00:00Z", "end": "2026-11-03T18:00:00Z" },
    "cutoffAt": "2026-11-03T14:00:00Z",
    "price": { "amountMinor": 9900, "vatMinor": 1980, "exVatMinor": 7920, "currency": "NOK" }
    }
    ]
    }

    Show these options to the shopper (see Showing options at checkout). Prices are what Delivr bills you, VAT included; what you charge the shopper is up to you.

  4. When the shopper pays, book the option they chose. Use your own order id as externalRef (here made unique with the time, so you can run the step again), and give the recipient’s name and phone so Delivr can text them a tracking link. The order’s id is saved to $ORDER.

    Request
    curl -s $DELIVR_API/v1/merchant/orders \
    -H "Authorization: Bearer $DELIVR_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $(uuid)" \
    -d @- <<EOF | tee order.json | jq .
    {
    "quoteId": "$QUOTE",
    "option": "on_demand",
    "externalRef": "order-$(date +%s)",
    "delivery": {
    "address": { "street": "Karl Johans gate 22", "postalCode": "0026", "city": "Oslo" },
    "contact": { "name": "Kari Nordmann", "phone": "+4791234567" },
    "dropoff": { "floor": 3, "doorCode": "1234" }
    }
    }
    EOF
    export ORDER=$(jq -r .id order.json)
    Response · 201
    {
    "id": "3f0e9a52-8a7e-4f8e-9a51-2c0f3c9d1b7a",
    "reference": "DD-482913",
    "externalRef": "order-1793710812",
    "type": "on_demand",
    "status": "awaiting_goods",
    "readyBy": "2026-11-03T14:00:12Z",
    "readyAt": "2026-11-03T13:10:12Z",
    "trackingUrl": "https://tracking-stage.delivr.no/t/…",
    "eta": { "from": "2026-11-03T13:25:00Z", "to": "2026-11-03T13:55:00Z" },
    "timeZone": "Europe/Oslo",
    "version": 3,
    "createdAt": "2026-11-03T13:00:12Z",
    "placedAt": "2026-11-03T13:00:12Z",
    "updatedAt": "2026-11-03T13:00:13Z"
    }

    The order’s id is the quoteId, and reference (DD-…) is what you quote to Delivr support.

  5. The store has packed the parcel. Tell Delivr, and the driver search starts at once. Order paths take Delivr’s id or the DD- reference.

    Request
    curl -s -X POST $DELIVR_API/v1/merchant/orders/$ORDER/ready \
    -H "Authorization: Bearer $DELIVR_API_KEY" \
    -H "Idempotency-Key: $(uuid)" | jq .status

    The order moves to searching. With the store’s default (readySignal: assumed) this step is optional: the goods count as ready after prepMinutes (10 minutes here), and the search starts then. See Goods ready signals.

  6. The sandbox has robo drivers parked at your store. One accepts within seconds, drives to the store, picks up and delivers. A delivery takes about two minutes. Poll the order to watch it move:

    Request
    while :; do
    STATUS=$(curl -s $DELIVR_API/v1/merchant/orders/$ORDER -H "Authorization: Bearer $DELIVR_API_KEY" | jq -r .status)
    echo "$(date +%T) $STATUS"
    case $STATUS in delivered | cancelled | failed) break ;; esac
    sleep 5
    done
    Output
    13:00:20 searching
    13:00:25 driver_assigned
    13:01:20 picked_up
    13:02:10 delivered

    Read the order once more and it has a proofOfDelivery. To test failures instead, put a directive such as sandbox:no_driver in pickup.note. See Sandbox and testing.

  7. If your webhook endpoint is set up, it received order.placed, order.searching, order.driver_assigned, order.picked_up and order.delivered, among others. Check the delivery log:

    Request
    curl -s "$DELIVR_API/v1/merchant/webhooks/deliveries?orderId=$ORDER" \
    -H "Authorization: Bearer $DELIVR_API_KEY" | jq '.items[] | {event, statusCode, succeeded}'

    Then verify the signatures in your handler. No endpoint yet? See Testing webhooks.