> ## Documentation Index
> Fetch the complete documentation index at: https://docs.interchange.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandbox Mode

> Test the full media buying lifecycle without real spend

## Overview

Sandbox mode lets you test the full media buying lifecycle — product discovery, campaign creation, creatives, execution, and delivery — without real platform calls or spending real money. Just create a sandbox advertiser and everything else is handled automatically.

<CardGroup cols={2}>
  <Card title="Safe Integration Testing" icon="shield-check">
    Validate your workflows end-to-end before going live. No real bids, no real spend.
  </Card>

  <Card title="Fully Automatic" icon="wand-magic-sparkles">
    Create a sandbox advertiser and you're done. Account routing and environment isolation are handled for you.
  </Card>
</CardGroup>

***

## How It Works

Sandbox is **account-level, not per-request**. The seller provisions a dedicated sandbox account, and every request using that account is automatically treated as sandbox. This eliminates the risk of accidentally mixing real and test traffic in a multi-step flow.

When you create an advertiser with `sandbox: true`:

* All discovered accounts for that advertiser are sandbox accounts
* The correct sandbox account is automatically injected into every ADCP call — `create_media_buy`, `get_media_buy_delivery`, and `get_products`
* Delivery and reporting data are fully scoped to the sandbox environment
* Responses contain simulated but realistic data

There is no additional configuration needed. You use the same endpoints and workflows as production — the only difference is the `sandbox: true` flag on the advertiser.

### Seller ad-server setup

For an ad-server-backed storefront, Interchange also needs a dedicated advertiser/account
inside the seller's ad server. Keep it separate from every production advertiser. The
seller can either:

* Create or designate a sandbox advertiser/account and assign it to the Interchange
  service account, then map it as the storefront's sandbox advertiser; or
* Grant the Interchange service account permission to create advertisers so Interchange
  can provision `Interchange - Sandbox` automatically.

Until one of those paths is complete, Interchange will recommend the setup in storefront
readiness and may send a reviewed seller Nudge. This recommendation does not block live
selling, but smoke tests will not run through a production or default advertiser as a
fallback.

<Info>
  For protocol-level details on how sandbox mode works, see the [AdCP Sandbox documentation](https://docs.adcontextprotocol.org/docs/media-buy/advanced-topics/sandbox#sandbox-mode).
</Info>

***

## Creating a Sandbox Advertiser

### Via API

Set `sandbox: true` in the create advertiser request body:

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://api.interchange.io/api/v2/buyer/advertisers" \
    -H "Authorization: Bearer your-api-key" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Acme Corp (Sandbox)",
      "description": "Integration testing advertiser",
      "sandbox": true
    }'
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch('https://api.interchange.io/api/v2/buyer/advertisers', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.SCOPE3_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      name: 'Acme Corp (Sandbox)',
      description: 'Integration testing advertiser',
      sandbox: true,
    }),
  })

  const { data } = await response.json()
  console.log('Sandbox advertiser:', data.id, data.sandbox) // sandbox: true
  ```

  ```python Python theme={null}
  import requests, os

  response = requests.post(
      'https://api.interchange.io/api/v2/buyer/advertisers',
      headers={
          'Authorization': f'Bearer {os.environ["SCOPE3_API_KEY"]}',
          'Content-Type': 'application/json',
      },
      json={
          'name': 'Acme Corp (Sandbox)',
          'description': 'Integration testing advertiser',
          'sandbox': True,
      },
  )

  advertiser = response.json()['data']
  print('Sandbox advertiser:', advertiser['id'], advertiser['sandbox'])
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "data": {
    "id": "adv_abc123",
    "name": "Acme Corp (Sandbox)",
    "description": "Integration testing advertiser",
    "status": "active",
    "sandbox": true,
    "createdAt": "2026-02-22T21:35:44Z",
    "updatedAt": "2026-02-22T21:35:44Z",
    "linkedBrand": null,
    "brand": null,
    "brandWarning": null
  }
}
```

### Via UI

When creating an advertiser in the dashboard, toggle the **Sandbox** switch before saving. Sandbox advertisers are shown with a badge in the advertiser list for easy identification.

<Warning>
  **Sandbox is permanent.** Once an advertiser is created with `sandbox: true`, the flag cannot be changed. This protects against accidentally switching an advertiser from sandbox to production after campaigns have been configured.
</Warning>

***

## Using Sandbox

Once you have a sandbox advertiser, the entire workflow is identical to production. Discover products, create campaigns, add creatives, and execute — all using the same API endpoints. The sandbox routing is completely transparent.

For example, executing a campaign:

```bash theme={null}
curl -X POST "https://api.interchange.io/api/v2/buyer/campaigns/{campaignId}/execute" \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json"
```

Because the campaign belongs to a sandbox advertiser, all ADCP calls are automatically routed through the sandbox environment. Responses are simulated — no real bids are placed and no real spend occurs.

***

## Murph Sandbox Test Plans

When Murph is enabled for an account, `GET /api/v2/murph/test-runs` returns the
latest sandbox test plans and diagnostics that appear in Murph's right rail.
This endpoint is scoped to the authenticated account and requires the
`ask-murph` feature flag.

For a seller-owned inventory-source test, give Murph the advertiser's brand
domain and currency during planning. Murph checks for a matching sandbox
advertiser inside that account and returns the existing advertiser ID when one
is available. The same brand-scoped sandbox account is used to preview products,
so the selected product IDs remain valid when the test runs. If no matching
advertiser exists, the read-only plan says that the confirmed execution will
create one; it never substitutes a production advertiser. Sellers do not need
buyer-side advertiser-list access or a previous test run to recover the ID.
This is a buyer advertiser owned by the currently authenticated organization;
it is not a hidden cross-organization identity or the seller-side advertiser
inside the connected ad server.

Query parameters:

| Param          | Notes                                                                                        |
| -------------- | -------------------------------------------------------------------------------------------- |
| `take`         | Number of plans to return, 1..20. Defaults to 5.                                             |
| `storefrontId` | Optional storefront scope for accounts with more than one storefront.                        |
| `runUid`       | Optional exact test-run UUID. Use this when following a diagnostics link from a failed test. |

Each item in `runs[]` includes the plan status (`planned`, `partial`, `passed`,
or `failed`), the current stage, sanitized `summary`, `artifacts`, and
`diagnostics`, plus timeline `steps[]` with operation names, HTTP status when
available, and a diagnostic message. Murph currently records buyer-stack plans
and approval-blocked attempts; executable buyer-stack runs require a
server-issued approval token flow.

When campaign execution fails, the tool result and stored run diagnostics
distinguish local execution preflight from the buyer-side dispatch boundary.
`executionAttempted: true` means the buyer stack evaluated a prepared media
buy. `dispatchAttempted: false` means that evaluation failed before a
`create_media_buy` dispatch began, so no seller call could have been issued. The
associated `executionErrors[]` entry contains the media-buy ID, sales-agent ID,
and specific preflight message.
`dispatchAttempted: true` proves only that the buyer-side boundary was reached;
an internal storefront can still fail before forwarding to its inventory
source. `mediaBuysAttempted` counts prepared buys evaluated by
`execute_campaign`; it does not by itself prove that a seller received a call.

`diagnostics.agentCalls` lists the per-seller results returned by the discovery
round-trip (`get_products`), including on runs that later fail at product
selection or the dispatch boundary — a downstream failure does not clear the
seller results discovery already returned. To confirm whether a seller was
reached on the wire regardless of what discovery returned, cross-check the
storefront activity log, which records the raw round-trip. These are safe
summaries only, not raw upstream responses.

When a Murph sandbox execution fails or stalls after recording a run, Murph
opens Test Runs directly on that run. Hosts that cannot embed the MCP App, such
as Slack, show an **Open diagnostics** button that opens the same exact-run
view. This handoff is driven by the typed tool result and its `runUid`, not by
matching words in Murph's answer.

In an app-capable host, select **Fix this run** to have Murph diagnose the exact
persisted failure and apply any safe repair available through typed tools. The
result, any error, and **Try again** stay inside that run's detail; the action
does not add a synthetic message to the host composer. If an external change or
human decision is still required, the inline result names that prerequisite
before you rerun the test.

***

## Filtering Sandbox Advertisers

The `sandbox` field is returned on every advertiser response. Use the optional `sandbox` query parameter to filter:

```bash theme={null}
# List only sandbox advertisers
curl "https://api.interchange.io/api/v2/buyer/advertisers?sandbox=true" \
  -H "Authorization: Bearer your-api-key"

# List only production advertisers
curl "https://api.interchange.io/api/v2/buyer/advertisers?sandbox=false" \
  -H "Authorization: Bearer your-api-key"
```

<Tip>
  In the dashboard, sandbox advertisers are shown with a **Sandbox** badge so they are easy to distinguish from production advertisers at a glance.
</Tip>

***

## Key Constraints

| Constraint             | Detail                                                                                                        |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| **Immutable flag**     | `sandbox` cannot be changed after advertiser creation                                                         |
| **Strict isolation**   | Sandbox and production data are fully isolated — no risk of mixing test and real traffic                      |
| **No silent fallback** | If sandbox execution cannot proceed, the API returns an explicit error rather than falling back to production |

***

## Next Steps

<CardGroup cols={3}>
  <Card title="Advertiser API Reference" href="/v2/buyer-api-reference" icon="code">
    Full schema for `POST /advertisers`, including the `sandbox` field.
  </Card>

  <Card title="AdCP Sandbox Docs" href="https://docs.adcontextprotocol.org/docs/media-buy/advanced-topics/sandbox#sandbox-mode" icon="book">
    Protocol-level details on how sandbox mode works in AdCP.
  </Card>

  <Card title="Quickstart" href="/v2/quickstart" icon="rocket">
    Get up and running with the Interchange API.
  </Card>
</CardGroup>
