# Quotes and orders

> Price the delivery options for an address, then book the one your shopper picks.

Source: https://docs.delivr.no/guides/quotes-and-orders/

Booking a delivery takes two calls: a quote prices the options, and an order books one of them.

## 1. Show Delivr in the cart (optional)

Before the shopper has entered their whole address, ask for a cheap hint by postal code:

```bash
curl "https://driver-staging-api.delivr.no/v1/merchant/availability?pickupExternalStoreId=oslo-sentrum&postalCode=0159" \
  -H "Authorization: Bearer $DELIVR_API_KEY"
```

```json
{
  "postalCode": "0159",
  "onDemand": { "available": true, "eta": { "fromMinutes": 35, "toMinutes": 60 } },
  "windows": { "available": true },
  "priceFrom": { "amountMinor": 9900, "currency": "NOK" },
  "timeZone": "Europe/Oslo",
  "checkedAt": "2026-11-03T13:00:00Z"
}
```

Use it for messages like "Delivered today from NOK 99". It promises nothing and is safe to cache for 60 seconds.

## 2. Quote

`POST /v1/merchant/quotes` prices every option from a store to an address.

| Field                                         | Required    | Notes                                                                                                                       |
| --------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------- |
| `pickupLocationId` or `pickupExternalStoreId` | One of them | The store.                                                                                                                  |
| `delivery.address`                            | Yes         | `street`, `postalCode`, `city`; optional `line2` (apartment, such as `H0101`) and coordinates.                              |
| `delivery.contact`                            | No          | The recipient. Required later, on the order.                                                                                |
| `parcels`                                     | Yes         | Groups of identical parcels: `count`, `weightKg`, and all three dimensions or none (none uses the store's `defaultParcel`). |
| `items`                                       | No          | Products bought (`title`, `quantity`, optional `imageUrl`). Shown to the recipient and driver, never priced.                |
| `readyBy`                                     | No          | When the goods are ready at the latest, within 24 hours.                                                                    |

The answer lists `options`, with `on_demand` first when there is driver capacity, then windows in time order:

- **On demand** has an `eta` band: when the recipient can expect the delivery.
- **Windows** have a `window` and a `cutoffAt`. Book and cancel for free before the cutoff.
- Every option has a stable `key`, such as `on_demand` or `window:2026-11-03T17:00:00+01:00/2026-11-03T19:00:00+01:00`. The same delivery quoted again gives the same keys, so you can store a key with the shopper's cart.
- `price` is what Delivr bills you, VAT included. You decide what the shopper pays.

`options` may be empty, for example far outside Delivr's area. If nothing at all can be offered you get `422 no_delivery_options`.

## 3. Order

When the shopper pays, `POST /v1/merchant/orders` with:

| Field              | Notes                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `quoteId`          | The quote.                                                                                                                                                   |
| `option`           | The `key` of the option chosen.                                                                                                                              |
| `externalRef`      | Your order id, unique among your live orders.                                                                                                                |
| `delivery`         | `address`, **`contact`** (the recipient's name and phone, required) and optional `dropoff` (`instructions`, `doorCode`, `floor`, `entrance`, `leaveAtDoor`). |
| `pickup.note`      | Optional note for the driver at the store, for this order only.                                                                                              |
| `parcels`, `items` | Optional. Replace the quote's.                                                                                                                               |
| `readyBy`          | Optional, on demand. When the goods are due.                                                                                                                 |
| `onWindowFull`     | Optional, windows. `reject` (default) or `reschedule`. See below.                                                                                            |

The answer is `201` with the order. Its `id` equals the `quoteId`, and `reference` (`DD-…`) is the short id for people. The placement answer also carries `eta` (on demand) or `rescheduled` (window); these two fields appear only there, so store them if you need them.

### Placing on an older quote

A quote is valid for **5 minutes** (`expiresAt`), plus a short grace when you place. After that, or when the address or parcels changed, Delivr quotes again behind the scenes and books the same key at **no more than the quoted price**.

| Answer                   | What happened                                                                                             | What to do                          |
| ------------------------ | --------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `201`                    | Booked, at or below the quoted price.                                                                     | Nothing.                            |
| `409 price_changed`      | It would cost more now. `details.option` has the new price.                                               | Show the new price, or quote again. |
| `409 option_unavailable` | The option is gone, for example no driver capacity. `details.alternatives` lists what is offered instead. | Offer an alternative.               |
| `409 window_full`        | The window filled up. `details.alternatives` has the nearest free window.                                 | Offer it, or set `onWindowFull`.    |

With `"onWindowFull": "reschedule"`, a full window books the nearest free one instead (no dearer than quoted). The answer has `rescheduled: true`, and you get an `order.window_changed` webhook.

### Booking without a quote

If your shopper does not pick between options, you can skip the quote: send the store (`pickupLocationId` or `pickupExternalStoreId`), `parcels` and the option key, with no `quoteId`. The order is quoted and booked in one call.

### Retries

Placing is idempotent twice over: by the `Idempotency-Key`, and by `quoteId` (or `externalRef` without a quote). A retry answers the order already placed instead of booking a second delivery.

## Who sees what

| Data                                      | Driver | Recipient (SMS, tracking) | Webhooks |
| ----------------------------------------- | ------ | ------------------------- | -------- |
| `items`                                   | Yes    | Yes                       | No       |
| `delivery.dropoff.doorCode`               | Yes    | No                        | No       |
| `pickup.note`, store `pickupInstructions` | Yes    | No                        | No       |