# Quickstart

> Add a store, quote a delivery, place an order and watch it get delivered in the sandbox.

Source: https://docs.delivr.no/quickstart/

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

> **Before you start:** You need a **sandbox API key** (`dmk_…`) with scope `orders`. See [Get sandbox access](/sandbox/#get-sandbox-access)
> for what to send us. Keys are shown once, so store yours in a secret manager.

```bash
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. ### Check your key

   [List your store locations](/api/operations/listlocations/). An empty list is fine; it means your key works.

   ```bash title="Request"
   curl -s $DELIVR_API/v1/merchant/locations \
     -H "Authorization: Bearer $DELIVR_API_KEY"
   ```

   ```json title="Response · 200"
   { "items": [] }
   ```

   A `401 unauthenticated` means the key is wrong or revoked. See [Authentication](/authentication/).

2. ### Add a store

   [Register the store](/api/operations/createlocation/) your goods are picked up from. Delivr finds it on the map from its address.

   ```bash title="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 .
   ```

   ```json title="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. ### Quote a delivery

   [Ask what Delivr can offer](/api/operations/createquote/) from the store to an address, for one 4.5 kg parcel.
   The answer is saved to `quote.json` and its id to `$QUOTE`.

   ```bash title="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)
   ```

   ```json title="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](/guides/checkout-options/)). Prices are
   what Delivr bills you, VAT included; what you charge the shopper is up to you.

   > **Tip:** No `on_demand` option? No robo driver is near the store yet. Wait a minute and quote again. If it still does not
   > show, see [Robo drivers](/sandbox/#robo-drivers).

4. ### Place the order

   When the shopper pays, [book the option](/api/operations/createorder/) 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`.

   ```bash title="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)
   ```

   ```json title="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. ### Say the goods are ready

   The store has packed the parcel. [Tell Delivr](/api/operations/markready/), and the driver search starts at once.
   Order paths take Delivr's `id` or the `DD-` reference.

   ```bash title="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](/guides/ready-signals/).

6. ### Watch a robo driver deliver it

   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:

   ```bash title="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
   ```

   ```text title="Output"
   13:00:20 searching
   13:00:25 driver_assigned
   13:01:20 picked_up
   13:02:10 delivered
   ```

   [Read the order](/api/operations/getorder/) 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](/sandbox/).

7. ### Receive the webhooks

   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](/api/operations/listwebhookdeliveries/):

   ```bash title="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](/webhooks/signatures/) in your handler. No endpoint yet? See
   [Testing webhooks](/webhooks/testing/).

## What to read next

- [Showing options at checkout](/guides/checkout-options/): how to present the quote to shoppers.
- [Quotes and orders](/guides/quotes-and-orders/): expired quotes, price changes and booking without a quote.
- [Order lifecycle](/guides/order-lifecycle/): every status and reason to handle.
- [Receiving webhooks](/webhooks/): ordering, retries and deduplication.
- [Go-live checklist](/go-live/): what to check before you switch to production.