> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.goautolane.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.goautolane.com/_mcp/server.

# Sandbox

> Build and test your Direct Delivery integration against a fully isolated sandbox environment, with synthetic autonomous vehicles you control from the Autolane Portal.

# Sandbox

The Autolane Sandbox is a fully isolated environment for integration development. Same `/dd/v1/*` API surface as production, same request/response shapes, same webhooks. The difference is that every delivery is fulfilled by a **synthetic autonomous vehicle** you control from the Autolane Portal instead of a real autonomous vehicle dispatched to a customer.

Use the sandbox to:

* **Build your integration end-to-end** before any real money or real autonomous vehicle dispatch is involved.
* **Drive a delivery through every lifecycle state** at your own pace, on demand, from the portal UI.
* **Test webhook handlers** against real Autolane-signed payloads with the canonical retry behavior.
* **Reproduce failure modes** (out of service area, customer opted out, geocode failure) deterministically.

## Getting started

### Base URL

```
https://api-sandbox.goautolane.com/dd/v1/*
```

The path surface is identical to production. Only the host changes.

### Creating a sandbox API key

1. Log in to the [Autolane Portal](https://portal.goautolane.com).
2. Switch to **dev mode** (toggle in the top navigation).
3. Open **Integrations** > **API Keys** in the sidebar.
4. Click **+ Create API Key**.
5. Give the key a name (e.g., "Sandbox dev"), select the permissions your integration needs (`deliveries:read`, `deliveries:write`, `webhooks:manage`), and click **Create**.
6. Copy the key. It is only shown once.

Sandbox keys can only be used against `api-sandbox.goautolane.com`. Using a sandbox key against the production host (or vice versa) returns `403 WRONG_ENV_KEY`.

### Your `x-retailer-id`

Use the site UUID shown in the Autolane Portal while it is in **dev mode**. The portal lists your organization's sites separately for each environment; the "dev mode" badge confirms which environment you're currently configuring. When you promote to production, check the production listing for the site you want to call against and use that UUID with your production key.

## Synthetic actors

Every sandbox retailer is pre-provisioned with **one synthetic vehicle and one synthetic supervisor driver**, both flagged with `is_synthetic = true` server-side. When you create a delivery in the sandbox:

* The pickup is **eagerly assigned** to your synthetic vehicle/driver pair at create time (in production, pickups sit in the driver queue until they're claimed for dispatch).
* The pickup is created in `SCHEDULED` status with `is_autonomous = true`.
* The order is tagged with `admin_test_order_meta.driver_mode = 'synthetic'` so no production dispatch path can ever reach it.

You will not see any of this assignment metadata in the public API response. The order behaves like a regular Direct Delivery order from your code's perspective. The difference shows up in the lifecycle: you advance it.

## Quoting and creating a delivery

The two-step quote-then-create flow is identical to production. A normal happy-path request looks the same:

```bash
curl -X POST https://api-sandbox.goautolane.com/dd/v1/deliveries/quote \
  -H "Authorization: Bearer YOUR_SANDBOX_API_KEY" \
  -H "x-retailer-id: a1b2c3d4-e5f6-7890-1234-567890abcdef" \
  -H "Content-Type: application/json" \
  -d '{
    "external_delivery_id": "SANDBOX-001",
    "customer": { "phone": "+15551234567" },
    "delivery_address": {
      "street": "123 Main St",
      "city": "Austin",
      "state": "TX",
      "zip": "78701"
    },
    "ready_by": "YOUR_FUTURE_ISO8601_UTC_TIMESTAMP",
    "order_items": [{ "name": "Latte", "quantity": 1 }]
  }'
```

```bash
curl -X POST https://api-sandbox.goautolane.com/dd/v1/deliveries \
  -H "Authorization: Bearer YOUR_SANDBOX_API_KEY" \
  -H "x-retailer-id: a1b2c3d4-e5f6-7890-1234-567890abcdef" \
  -H "Content-Type: application/json" \
  -d '{ "external_delivery_id": "SANDBOX-001" }'
```

## Simulating failure modes

Every `unavailable_reason` the production API can return is reachable in the sandbox by varying request input. No special flag, no admin lever; just the same logic.

| Failure scenario                | How to trigger it in sandbox                                                                                                                                                                                                      |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OUT_OF_SERVICE_AREA`           | Quote to an address whose lat/lng falls outside your retailer's configured service-area polygon.                                                                                                                                  |
| `NO_DRIVERS_ONLINE`             | Set a `ready_by` far enough in the future that no driver ETA can satisfy it; the quote returns `available: false`.                                                                                                                |
| `CUSTOMER_OPTED_OUT`            | Quote against a phone number that has previously sent an opt-out reply to an Autolane SMS in this sandbox.                                                                                                                        |
| `NO_CAPACITY`                   | Create deliveries until the sandbox vehicle has an active run plus one queued run (each create is eagerly assigned to your synthetic vehicle), then quote again. Releases as deliveries advance or are cancelled from the portal. |
| `GEOCODE_FAILED` (422)          | Submit a `delivery_address` whose fields cannot be geocoded (random street name in a small zip) without an explicit `location` override.                                                                                          |
| `INVALID_READY_BY` (400)        | Submit a `ready_by` timestamp in the past.                                                                                                                                                                                        |
| `RETAILER_NOT_CONFIGURED` (503) | Use an `x-retailer-id` for a site whose `service_area` or `delivery_price_cents` is not yet set.                                                                                                                                  |
| `RETAILER_INACTIVE` (403)       | Use an `x-retailer-id` for a site that has been deactivated in the portal.                                                                                                                                                        |
| `WRONG_ENV_KEY` (403)           | Send a production API key against `api-sandbox.goautolane.com`, or vice versa.                                                                                                                                                    |

## Driving a delivery through the lifecycle

Once a sandbox delivery is created, you advance it through pickup states from the **Autolane Portal**. Open the delivery in the portal's Test Deliveries view, then use the **Advance** button to step the synthetic pickup forward one state at a time:

```
SCHEDULED → ASSIGNED → EN_ROUTE_TO_STORE → ARRIVED_AT_STORE →
  WAITING_FOR_LOAD → LOADED → EN_ROUTE_TO_CUSTOMER →
  ARRIVED_AT_CUSTOMER → COMPLETED
```

Each click advances exactly one state and fires the corresponding `pickup_status.changed` webhook at your registered URL (if you've subscribed to the event). You can also cancel from the portal at any non-terminal state before `LOADED`, which fires `pickup_status.changed` with `status: "CANCELLED"`. At `LOADED`, `EN_ROUTE_TO_CUSTOMER`, or `ARRIVED_AT_CUSTOMER`, the Cancel action starts a return when returns are enabled and answers `409` otherwise; see [Returns in the sandbox](#returns-in-the-sandbox) below. With returns enabled for your sandbox site, a repeat cancel on an already-cancelled pickup is also accepted (a no-op, no new webhook), and a cancel on a `FAILED` pickup is accepted and recorded as `CANCELLED`, matching the `PATCH` endpoint; without returns enabled both still answer `409`.

When the next step is `ARRIVED_AT_STORE`, the drawer offers an optional **Stall ID** field. Enter one to see `stall_id` in the resulting `pickup_status.changed` payload. The field appears in the webhook only if your sandbox site has stall reporting enabled; ask your Autolane contact to turn it on for your sandbox retailer.

### Returns in the sandbox

With returns enabled for your sandbox site (ask your Autolane contact), send `PATCH /dd/v1/deliveries/{external_delivery_id}` with `{ "status": "CANCELLED" }` while the delivery is `LOADED`, `EN_ROUTE_TO_CUSTOMER`, or `ARRIVED_AT_CUSTOMER`, the same call your production integration makes. The response is `200` with `status: "RETURN_IN_PROGRESS"` and that webhook fires, with `return_reason` normalized from the optional `cancellation_reason`: `CUSTOMER_CANCELLED` and `COLD_CHAIN_EXPIRED` are kept, any other value becomes `OTHER`, and no reason leaves it `null`. The drawer then steps the return: **Advance to At store (return)** offers an optional **Return stall ID** (it appears in the payload as `return_stall_id` only when your sandbox site has stall reporting enabled), and **Advance to Returned** sends `RETURNED` with `return_fee_cents`. The drawer's Details block shows the return reason, the return stall, and the return fee. Without returns enabled, a cancel at `LOADED` or later answers `409`, as in production. The drawer's **Cancel** action starts a return the same way once the delivery is loaded.

The delivery's top-level `status` on `GET /dd/v1/deliveries/{external_delivery_id}` advances in lockstep — the GET response and the webhook stream always agree on the same lifecycle value.

There is no public API endpoint for advancing a sandbox pickup. The advance flow is intentionally portal-only so that integrations exercise their webhook handlers, not a server-driven simulation loop.

## Testing webhooks

Webhook delivery in sandbox is the **same code path as production**: same `X-Autolane-Signature` HMAC-SHA256 over the body, same `X-Autolane-Delivery-Id` UUID stable across retries, same 5-attempt 30-second-base exponential backoff, same 50-consecutive-failure auto-disable + email notification.

Recommended workflow:

1. Register your sandbox webhook against your dev environment (an `ngrok` or `cloudflared` tunnel to localhost is fine; the URL just needs to be HTTPS).
2. Subscribe to `pickup_status.changed` (and `pickup.vehicle_location.changed` if you're building a live-map UI).
3. Create a delivery in sandbox.
4. Advance the pickup from the portal one state at a time, verifying that each `pickup_status.changed` arrives at your endpoint, your signature verification passes, and your idempotency logic handles re-delivered events correctly.

The portal's Test Deliveries view also shows a per-delivery event log. Each row is one outbound webhook attempt (HTTP status, response body, latency, error message if any) so you can correlate what we sent against what your endpoint received.

## Differences from production

| Aspect                         | Production                                                                                                                                                  | Sandbox                                                                                                                                                        |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Base URL                       | `https://api.goautolane.com/dd/v1/*`                                                                                                                        | `https://api-sandbox.goautolane.com/dd/v1/*`                                                                                                                   |
| API key environment marker     | `env:production`                                                                                                                                            | `env:sandbox`                                                                                                                                                  |
| Pickup driver assignment       | Pickup queued; claimed when an autonomous vehicle (or a site supervisor on supervisor-equipped sites) is available.                                         | Pickup eagerly assigned to your retailer's synthetic vehicle + supervisor driver.                                                                              |
| `is_autonomous` on the pickup  | `true` for an autonomous delivery; `false` when a supervisor in the vehicle handles delivery (only available on sites equipped with a supervisor).          | Always `true` (the synthetic vehicle is autonomous).                                                                                                           |
| Lifecycle advancement          | Driven by real-world autonomous vehicle telemetry (or supervisor actions on supervisor-equipped sites).                                                     | Driven by you from the portal's Advance button. One state per click.                                                                                           |
| Store arrival                  | Autolane's live delivery lifecycle, usually vehicle telemetry crossing the store geofence. On sites with stall reporting the event also carries `stall_id`. | The portal Advance click into `ARRIVED_AT_STORE`, with an optional Stall ID.                                                                                   |
| Return check-in                | Autolane's live delivery lifecycle when the vehicle is back at the store. On sites with stall reporting the event also carries `return_stall_id`.           | The portal Advance click into `RETURN_ARRIVED`, with an optional Return stall ID.                                                                              |
| Customer SMS / notifications   | Live, sent to the real customer phone.                                                                                                                      | Suppressed for synthetic pickups so test runs don't text real numbers.                                                                                         |
| Vehicle location event payload | Real vehicle GPS samples streamed continuously.                                                                                                             | Synthetic location fixes recorded at each portal advance (the vehicle moves to the store on `ARRIVED_AT_STORE` and to the customer on `EN_ROUTE_TO_CUSTOMER`). |
| Webhook delivery semantics     | Production semantics (signature, retries, auto-disable).                                                                                                    | Identical.                                                                                                                                                     |
| Billing                        | Per-delivery charge applied at COMPLETED.                                                                                                                   | Free.                                                                                                                                                          |

## Promotion to production

Promotion to production includes completing all 7 of the onboarding steps in the portal, then clicking the **Request Production Access** button. Our onboarding team will review your integration within 24 hours.

Once approved, switch your integration over:

1. **Create a production API key** in the portal (switch the dev-mode toggle off, then **Integrations** > **API Keys** > **+ Create API Key**). Production keys carry the `env:production` marker and only work against `https://api.goautolane.com`.
2. **Update your production environment** with the new key. Sandbox keys won't work against the production host (`403 WRONG_ENV_KEY`).
3. **Re-register your production webhook** against your production webhook URL. Sandbox and production webhook subscriptions are separate; registering one does not create the other. Use a different signing secret per environment.
4. **Walk through every webhook handler at least once against the production host** by running a low-risk first delivery through. The retry and signature behavior is identical to sandbox, but a real autonomous vehicle (or a supervisor on supervisor-equipped sites) is now involved.
5. **Confirm `x-retailer-id`** maps to the production site UUID. The portal's production view and dev-mode view list sites separately; do not reuse a sandbox UUID against the production host. Pull the UUID from the production listing.

If anything looks different between sandbox and production behavior, please report it; the contract is meant to be byte-identical except for the items in the table above.