Skip to content

Sandbox and testing

The sandbox is a separate Delivr environment to build and test against. It behaves like production, with three differences:

  • No real SMS. Recipient messages are recorded, not sent. Use any valid Norwegian phone number, such as +4791234567, and open the order’s trackingUrl to see what the recipient sees.
  • Robo drivers. Test drivers deliver your on-demand orders in about two minutes.
  • Test data only. Never send real customers’ names, phones or addresses.
Base URL https://driver-staging-api.delivr.no
Tracking pages https://tracking-stage.delivr.no
Keys Sandbox keys (dmk_…), see Get sandbox access. They never work in production.

Ask your Delivr contact for a sandbox merchant. Send:

  • your company name and organization number, and the market (Norway),
  • who should receive the key,
  • the scope you need: orders to book deliveries, or quotes for a backend that only prices them (see Scopes),
  • your webhook URL, if you want webhooks (public HTTPS),
  • the fallback policy you want to test, if not the default recipient_choice.

Delivr creates a sandbox merchant and sends you the API key and, with a webhook URL, its signing secret (whsec_…). Each is shown once, so store them in a secret manager.

Only Delivr can change your webhook URL, so use one that does not change:

  • a tunnel with a fixed name, such as an ngrok static domain or a Cloudflare named tunnel, to reach your laptop,
  • or a request inspector such as webhook.site, to look at events before you have a handler.

A free ngrok URL changes every time you restart it. Until your endpoint is set up, test your handler locally.

  1. Add your stores with POST /v1/merchant/locations. Robo drivers park at a new store within a minute.
  2. Check your webhook endpoint with GET /v1/merchant/webhook (409 conflict means none is set up yet).
  3. Send a test event with POST /v1/merchant/webhooks/test, then GET /v1/merchant/webhooks/deliveries to see what your server answered.

Robo drivers park outside sandbox stores, online. Because they are nearby, quotes for your store offer on_demand, and when an order’s goods are released one of them accepts it.

A robo driver behaves like a real driver using the Delivr driver app:

Step Takes Status and webhook
Accepts the offer A few seconds after the search starts driver_assigned, order.driver_assigned
Drives to the store About 45 seconds
Arrives and loads, with a pickup photo 5 to 20 seconds picked_up, order.picked_up
Drives to the recipient About 45 seconds
Delivers, with a drop-off photo delivered, order.delivered with proofOfDelivery

The recipient’s tracking page shows the robo driver moving on the map, just as a shopper would see a real one.

The sandbox is shared by every integrator, and so are its robo drivers:

  • Stores are grouped into cells of about 100 m. Stores of different merchants at the same address share a cell.
  • Two robo drivers park in each of the 30 newest cells, counted across all integrators. They return there after each delivery.
  • Robo drivers only take Merchant API orders.

So two things can happen:

  • Both drivers of your cell are busy, for example because another integrator uses the same address. A quote then has no on_demand option until one is back. Wait a few minutes, or use a store at another address below.
  • Your store has dropped out of the 30 newest cells because others added stores since. Its quotes stop offering on_demand. Add a new location (a new externalStoreId) at another address, and robo drivers park there within a minute. Locations cannot be removed, so give test stores clear names.

These Oslo addresses are in the national address register, so they work without coordinates. Each store address is in its own cell.

Use as a store Use as a delivery address
Akersgata 45, 0158 Oslo Karl Johans gate 22, 0026 Oslo
Storgata 1, 0155 Oslo Møllergata 20, 0179 Oslo
Pilestredet 48, 0167 Oslo Dronning Eufemias gate 16, 0191 Oslo
Hegdehaugsveien 31, 0352 Oslo
Bogstadveien 50, 0366 Oslo
Grønlandsleiret 25, 0190 Oslo

An address that is not in the register answers 422 invalid_address. Check the postal code: Karl Johans gate 22 is 0026, not 0159.

Real drivers read pickup.note. Robo drivers do too, and they follow directives written there. Put one anywhere in the note of the order you place:

{
"quoteId": "…",
"option": "on_demand",
"externalRef": "test-no-driver-1",
"pickup": { "note": "sandbox:no_driver" },
"delivery": { "…": "…" }
}
Directive What the robo driver does What you can test
(none) Accepts and delivers. The happy path and every status webhook.
sandbox:no_driver Every robo driver declines, and the search ends within seconds instead of after 15 minutes. failed with reason no_driver, and your fallback policy.
sandbox:hand_back The first driver accepts, sets off and gives the job back. Another driver then delivers it. A driver handing a job back. For assisted checkout, sale.driver shows reassigning.
sandbox:stop_en_route Accepts and stays on the way to the store. Cancelling at the driver_en_route fee.
sandbox:stop_at_pickup Accepts, arrives at the store and waits there. Cancelling at the driver_arrived fee, and goods that are not ready when the driver arrives.

Directives are not case-sensitive and can sit next to normal text, like "Ask for Kari. sandbox:stop_at_pickup". A store’s pickupInstructions work too, which is handy for a store you only use to test one path. In production, directives are ordinary text that drivers ignore.

A held driver (stop_*) gives the job back after an hour if you do nothing. The order then searches again, and the next robo driver reads the same directive and holds it too. Cancel the order to end the test.

These are the defaults. A market’s price list or your agreement can set others.

What Default
Quote offered (expiresAt) 5 minutes
Quote grace when you place after expiry 10 minutes
On-demand driver search 15 minutes, then failed with no_driver
Ready grace after readyBy 15 minutes, then failed with goods_not_ready
Checkout budget (assisted checkout) 10 minutes from the driver’s accept
Recipient’s window choice (fallback) 30 minutes (choiceDeadline)
Scenario How
Goods not ready Set your store’s readySignal to required, place an order with a readyBy 5 minutes ahead, and send no ready or processing. You get order.ready_overdue at readyBy, then the order fails with goods_not_ready after the ready grace (15 minutes by default).
Packing time Send POST /orders/{id}/processing {readyAt} a few minutes ahead. The driver search starts so that the driver arrives at about readyAt.
Price changes Place an order on a quote more than 15 minutes old (validity plus grace). Delivr quotes again: you get the order at no more than the quoted price, or 409 price_changed.
Free cancellation Place an order and cancel it before you send ready, while it is still awaiting_goods, with maxFeeMinor: 0.
Cancellation fees Place with sandbox:stop_en_route or sandbox:stop_at_pickup, send ready, wait for driver_assigned, then check GET /orders/{id}/cancellation and cancel.
Fallback as recipient Place with sandbox:no_driver and send ready. Under recipient_choice, open the failed order’s trackingUrl and choose a window, as the recipient would.
Webhook retries Answer 500 from your endpoint, then look at GET /v1/merchant/webhooks/deliveries for the retry schedule. Replay an event with POST /v1/merchant/webhooks/events/{id}/replay.
Assisted checkout Place with sale: "pending", poll until sale.driver is assigned, then complete with POST /orders/{id}/sale.

Window orders can be quoted, placed, rescheduled and cancelled in the sandbox. Robo drivers do not run window routes, so a window order usually stays scheduled. If you need to see picked_up and delivered on a window order, ask your Delivr contact to run one for you.

  • The same rate limits apply as in production.
  • The sandbox is shared by every integrator, and Delivr may reset its data. Do not depend on old orders being there.
  • Prices come from a sandbox agreement and are not your production prices.