> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/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, Apostra 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 Apostra
  service account, then map it as the storefront's sandbox advertiser; or
* Grant Apostra service account permission to create advertisers so Apostra
  can provision `Apostra - Sandbox` automatically.

Until one of those paths is complete, Apostra 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.apostra.com/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.apostra.com/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.apostra.com/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.apostra.com/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.

### Media Company V3 preview

Every Media Company can create a
sandbox Advertiser, Campaign, Creative, Proposal request, and staged MediaBuy
through `/mcp/v3` without switching to a Buyer account. The preview exposes
`save_advertiser`, `save_campaign`, `save_creative`,
`save_creative_collection`, `request_proposals`, and `save_media_buy` in the
Seller account.

No entitlement or beta grant is required for this no-spend workflow. Live
Media Company-managed campaigns use the same tools but are a separate,
customer-scoped rollout controlled by the `amc-campaign-management` flag.
Live operations retain normal approval, creative, financial, publisher, and
inventory-source readiness checks; the flag does not bypass them. Giving
clients access to operate their own campaigns is separate and uses the
`storefront-self-service-buyers` rollout. Neither flag expands an Advertiser's
durable Storefront scope.

This path is deliberately confined:

* `save_advertiser` creates a no-spend Advertiser with `sandbox: true`; when the
  live rollout is enabled for the organization, omitting `sandbox` creates a
  live Advertiser;
* the advertiser and campaign remain owned by the authenticated Media Company;
* Campaign creation is pinned server-side to the company's own Storefront,
  even when `sellerIds` is omitted; and
* Proposal requests can address only that Storefront;
* Proposal acceptance and direct MediaBuy staging reject foreign qualified
  Proposal and Product IDs; and
* existing MediaBuy updates require every line item to resolve durably to that
  Storefront.

Integrated buyer `search` and `get` can inspect an authorized sandbox or live
Advertiser, Campaign, Creative, Proposal, and MediaBuy. Opening the company's
own Seller with `include: ["products"]` returns its wholesale Products,
including any eligible `signal_targeting_options`; pass a selected Signal
through that Product's `targetingOverlay` when staging the MediaBuy. After launch,
`get_delivery({ report: "campaign_delivery", ... })` queries bounded Buyer
delivery by explicit date range and own-supply advertiser, campaign, or media
buy scope.

Seller Accounts also get a route-backed organization workspace
selector. Inventory keeps the existing seller experience; Campaigns lists the
account's sandbox Advertisers, plus its live own-supply Advertisers once the
account is enrolled in `amc-campaign-management`, and opens the shared
Campaigns and Creative Pages. The native roster read stays fail-closed to
sandbox for an unenrolled account; it does not grant general Buyer REST
access. Two additional read-only compatibility calls hydrate those shared
Pages: Campaign listing requires an explicit, durably bound `advertiserId` in
either environment and removes Campaigns outside own supply; the promoted
Creative list requires the same bound Advertiser and exposes no attach or
generation actions. Advertiser, Campaign, and Creative detail, mutations, and
reporting remain V3-only for the Seller Account. Agents appears in the same
selector when the organization separately has Agent workspace access. Within a
browser session, returning to Campaigns restores the last Advertiser that was
successfully resolved from that roster; an invalid or foreign Advertiser ID
returns to account scope instead.

Creating an Advertiser or requesting Reporting from the Campaigns workspace
stages a Murph request, so the guarded V3 flow remains the only write and
delivery path — the workspace's "+ Add advertiser" control stages a
sandbox-creation prompt today; an enrolled account can ask Murph directly to
create a live Advertiser. Marketplace, Connections, Buyer Setup, and unbound
production Advertisers remain outside this preview.

Independent Buyer Accounts and their existing sandbox workflows are unchanged.

***

## Validate an Agent in sandbox

Agent validation now uses the Agent's public `validationSkill` and the ordinary
V3 Buyer workflow. Resolve the exact Agent with `get({ kind: "agent" })`, select
a profile from its returned plan, fetch the versioned skill, and follow that
skill without substituting legacy test wrappers or generic V2 `api_call`
orchestration.

Run the profile with sandbox inputs first. The workflow uses the same typed V3
objects and operations a Buyer uses: Advertiser, Campaign, Creative, Proposal,
MediaBuy, and bounded delivery reads. It preserves normal confirmation before
mutations and never treats a simulated run as certification.

After the run or an intentional stop, read the same Agent with
`include: ["validationRuns", "diagnostics"]`. Pass an exact
`validationRunId` to inspect its bounded trace. If the run selected Products
from a Source, open that Source's diagnostics as well. Report which assertions
were observed, failed, unavailable, or unexercised, and report cleanup
separately.

Historical Murph test-run records remain readable during the bounded rollback
window, but they are no longer a launch surface or readiness authority. Agent
validation evidence and Source diagnostics are the canonical history.

***

## 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.apostra.com/api/v2/buyer/advertisers?sandbox=true" \
  -H "Authorization: Bearer your-api-key"

# List only production advertisers
curl "https://api.apostra.com/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 Apostra API.
  </Card>
</CardGroup>
