> For the complete documentation index, see [llms.txt](https://docs.tryterra.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tryterra.co/vantage-api-docs/getting-started/working-with-sandbox.md).

# Working with Sandbox

The sandbox environment (`https://vantage-sandbox.tryterra.co`) lets you exercise the entire integration - ordering, fulfilment webhooks, kit activation, results, and acknowledgment - without shipping a physical kit. Webhooks are delivered to your configured endpoint exactly as in production.

There are two ways to move a sandbox order through its lifecycle:

1. **The simulate endpoint** - drive any lifecycle event on demand. Deterministic, scriptable, ideal for automated tests.
2. **The simulated supplier flow** - the sandbox supplier automatically progresses your order on timers, including the end-user kit-activation step. Ideal for experiencing the realistic journey.

Both start the same way: place an order as shown in [Ordering your first test](/vantage-api-docs/getting-started/ordering-your-first-test.md).

## Option 1: The simulate endpoint

`POST /api/v1/orders/{orderID}/simulate` applies one lifecycle event to your order exactly as a real supplier update would: the status history is recorded and the corresponding **signed webhook is delivered** to your registered endpoint.

```bash
curl --request POST \
  'https://vantage-sandbox.tryterra.co/api/v1/orders/251285377984405504/simulate' \
  -u 'YOUR_DEV_ID:YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{ "event": "delivery_fulfilled" }'
```

```json
{ "order_id": "251285377984405504", "applied_event": "delivery_fulfilled" }
```

* `event` is one of: `payment_complete`, `payment_failed`, `processing`, `delayed`, `cancelled`, `delivery_fulfilled`, `completed` (order events), `kit_activated`, `sample_processing_in_lab`, `sample_rejected`, `partial_results_ready`, `results_ready`, `lab_processing_error`, `escalation_raised` (result events).
* Result events target an order item; pass `"order_item_id"` to pick one when the order has several.
* Only valid forward transitions are accepted - an out-of-order event returns `422` (see the lifecycle graphs in [Core concepts](/vantage-api-docs/core-concepts.md#status-lifecycles)).
* `results_ready` also stores a sample result document, so the download and acknowledge flows below work end to end.

{% hint style="info" %}
The simulate endpoint exists **only outside production** - calling it in production returns `403`.
{% endhint %}

A minimal happy path for a fresh `AT_HOME` order (which starts at `order.payment_processing`):

```
payment_complete → processing → delivery_fulfilled → kit_activated → sample_processing_in_lab → results_ready
```

then fetch and acknowledge the result (steps 4-5 below).

## Option 2: The simulated supplier flow

The sandbox supplier automatically progresses orders the way a real kit journey would:

### 1. Shipping

Within a minute or so of placing the order you receive `order.status_changed` webhooks, ending with `order.delivery_fulfilled` carrying a tracking number and - **sandbox only** - the `supplier_item_id` you'll need for activation:

{% tabs %}
{% tab title="Sandbox" %}

```json
{
  "event_type": "order.status_changed",
  "event_id": "249956485092777984",
  "timestamp": 1763661470,
  "data": {
    "order_id": "251285377984405504",
    "status": "order.delivery_fulfilled",
    "supplier_item_id": "249602021676720128",
    "tracking_number": "KnD3d5PMZyq5ulNcWkrq"
  }
}
```

{% endtab %}

{% tab title="Production" %}

```json
{
  "event_type": "order.status_changed",
  "event_id": "249956485092777984",
  "timestamp": 1763661470,
  "data": {
    "order_id": "251285377984405504",
    "status": "order.delivery_fulfilled",
    "tracking_number": "KnD3d5PMZyq5ulNcWkrq"
  }
}
```

{% endtab %}
{% endtabs %}

### 2. Kit activation (end-user step)

Some suppliers require the end user to activate their kit before the lab will process it - typically by scanning a QR code on the packaging that opens an activation page. To stand in for your end user scanning the QR code, open the activation URL in a browser, using the `supplier_item_id` from the webhook above as `kit_id`:

```
https://vantage-sandbox.tryterra.co/api/v1/orders/activate?kit_id=249602021676720128
```

Fill out the test-taker details on the page and activate the kit. A successful activation:

* emits a `results.kit_activated` webhook containing the newly assigned **`test_taker_id`** - capture it, you need it to fetch and acknowledge results;
* moves the order item to `results.kit_activated`.

<div><figure><img src="/files/yxW5S6by6zUVogoXYCHt" alt="Kit activation form"><figcaption></figcaption></figure> <figure><img src="/files/r9zaUY00SMSbqtKRQSfN" alt="Kit activation confirmation"><figcaption></figcaption></figure></div>

(In production the QR code embeds the same URL on `vantage.tryterra.co`; the browser visit is only a sandbox stand-in for the scan. Activation can also be performed programmatically - `POST /api/v1/orders/activate` - if you build your own activation UI.)

### 3. Lab processing and results

After activation, the sandbox automatically sends the remaining result webhooks at 1-2 minute intervals:

{% tabs %}
{% tab title="Lab processing" %}

```json
{
  "event_type": "order_item.results_status_change",
  "event_id": "249958648347009024",
  "timestamp": 1763661985,
  "data": {
    "order_id": "251285377984405504",
    "order_item_id": "251285377984405507",
    "results_status": "results.sample_processing_in_lab",
    "variant_id": "100041",
    "test_taker": {
      "test_taker_id": "257837964552478720",
      "first_name": "John",
      "last_name": "Doe",
      "email": "john.doe@example.com",
      "phone_number": "+14155551234"
    }
  }
}
```

{% endtab %}

{% tab title="Results ready" %}

```json
{
  "event_type": "order_item.results_status_change",
  "event_id": "249958796259139584",
  "timestamp": 1763662021,
  "data": {
    "order_id": "251285377984405504",
    "order_item_id": "251285377984405507",
    "results_status": "results.results_ready",
    "variant_id": "100041",
    "test_taker": {
      "test_taker_id": "257837964552478720",
      "first_name": "John",
      "last_name": "Doe",
      "email": "john.doe@example.com",
      "phone_number": "+14155551234"
    }
  }
}
```

{% endtab %}
{% endtabs %}

The sandbox happy path delivers `results.results_ready`; use the simulate endpoint (Option 1) to exercise `results.sample_rejected` and the other failure paths deliberately.

### 4. Fetching results

Fetch a presigned download URL with the `order_item_id` and the `test_taker_id` (both **required**):

{% tabs %}
{% tab title="cURL" %}

```bash
curl 'https://vantage-sandbox.tryterra.co/api/v1/results/251285377984405507?test_taker_id=257837964552478720' \
  -u 'YOUR_DEV_ID:YOUR_API_KEY'
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

order_item_id = "251285377984405507"
test_taker_id = "257837964552478720"
url = f"https://vantage-sandbox.tryterra.co/api/v1/results/{order_item_id}"

response = requests.get(
    url,
    auth=("YOUR_DEV_ID", "YOUR_API_KEY"),
    params={"test_taker_id": test_taker_id},
)
print(response.text)
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	orderItemID := "251285377984405507"
	testTakerID := "257837964552478720"
	url := fmt.Sprintf(
		"https://vantage-sandbox.tryterra.co/api/v1/results/%s?test_taker_id=%s",
		orderItemID, testTakerID,
	)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		panic(err)
	}
	req.SetBasicAuth("YOUR_DEV_ID", "YOUR_API_KEY")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(body))
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="200" %}

```json
{
  "download_url": "https://storage.googleapis.com/.../normalised.json?X-Goog-Signature=...",
  "format": "json",
  "expires_at": "2025-11-19T19:08:04.400Z"
}
```

{% endtab %}
{% endtabs %}

The `download_url` is valid for **15 minutes**. Re-call this endpoint to mint a fresh URL rather than storing it. The downloaded document is a FHIR Bundle - see [Results](/vantage-api-docs/documentation/results.md).

### 5. Acknowledge the results

After your end user has actually viewed their results, record their confirmation (again with both `order_item_id` and `test_taker_id`):

```bash
curl --request POST \
  'https://vantage-sandbox.tryterra.co/api/v1/results/251285377984405507/acknowledge?test_taker_id=257837964552478720' \
  -u 'YOUR_DEV_ID:YOUR_API_KEY'
```

```json
{ "status": "acknowledged" }
```

{% hint style="danger" %}
Acknowledgment is a mandatory compliance step with liability consequences, and it must be triggered by an explicit end-user action - never automatically. Read [Acknowledging Results](/vantage-api-docs/important-information/acknowledging-results.md) before building this flow.
{% endhint %}
