> ## 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.

# Migrating from v1 to v2

> Guide for upgrading from the v1 Apostra API to v2

# Migrating from v1 to v2

The v2 Apostra API is a fundamental simplification of v1. The surface is smaller, the conventions are agent-first, and the platform now extends across both the buy side and the sell side. v1 is frozen — every integration must migrate to v2. This guide walks you through what changed and how to translate the calls you already use today.

***

## Overview

v2 is a redesign — not a refactor. The core ideas behind Apostra (allocation over bidding, agentic orchestration, signal-driven media quality) are unchanged. What changed is the API contract.

<CardGroup cols={2}>
  <Card title="Simpler surface" icon="layer-group">
    Single endpoint, single object, full intent (SESOFI). RESTful resources replace v1's per-action verb endpoints.
  </Card>

  <Card title="Agent-first MCP" icon="robot">
    Two generic MCP wrappers — `api_call` and `ask_about_capability` — replace dozens of per-domain MCP tools.
  </Card>

  <Card title="AAO marketplace" icon="store">
    Native support for the Agentic Advertising Organization (AAO) — discover and buy from storefronts.
  </Card>

  <Card title="Measurement engine" icon="chart-line">
    First-class endpoints for hypotheses, test plans, learning cycles, and measurement data ingestion.
  </Card>
</CardGroup>

***

<Warning>
  **v2 is required.** v1 is frozen and will be retired — every integration must move to v2. This guide is the path.
</Warning>

<Tip>
  You can migrate incrementally: v1 and v2 run side by side, so you can move one advertiser, campaign, or workflow at a time rather than cutting over in a single shot.
</Tip>

***

## Key conceptual changes

### Brand Agent → Advertiser

v1 used the term **Brand Agent** as the top-level container for campaigns, creatives, and brand stories. v2 calls this an **Advertiser**. The model is the same, the name is industry-standard.

| v1 term | v2 term |
| - | - |
| Brand Agent | Advertiser |
| Brand Agent Standard | Advertiser Standard (rolled into measurement / discovery) |
| Tactic | Media Buy (already in v1, now consistent) |
| Outcome Agent | Measurement Engine + Hypotheses + Test Plans |

### MCP tool model: per-domain → generic

In v1, each domain had its own MCP tool — `brand-agent-create`, `campaign-update`, `media-buy-execute`, and so on. Agents had to memorize dozens of tool names and argument shapes.

In v2, every operation goes through one of three generic tools:

| Tool | Purpose |
| - | - |
| `health` | Probe API connectivity |
| `ask_about_capability` | Ask the API in natural language what it can do — returns operations, fields, and display requirements |
| `api_call` | Execute any REST endpoint via a named `operation`, with `pathParams`, `body`, and `params` |

The agent discovers capabilities at runtime via `ask_about_capability` and then dispatches through `api_call`. There is nothing to register, nothing to update when the API gains new operations.

### Skill files

Both buyer and storefront roles ship a skill file that fully describes the API for any agent that can read Markdown:

| Role | Skill name | Skill URL |
| - | - | - |
| Buyer | `scope3-agentic-buyer` (v2.0.0) | `https://api.apostra.com/api/v2/buyer/skill.md` |
| Storefront | `scope3-agentic-storefront` (v2.0.0) | `https://api.apostra.com/api/v2/storefront/skill.md` |

Point a custom agent at the skill file and it has end-to-end understanding of the API — operations, required fields, display rules, and workflow guidance.

### New surfaces in v2

| Surface | What it is |
| - | - |
| **Storefronts** | Sell-side companion API for inventory owners listing products to buyers |
| **Measurement engine** | Hypotheses, test plans, learning cycles, belief state, and feedback loops |
| **AAO compliance** | Native Agentic Advertising Organization (AAO) support — discover and authenticate against storefronts |
| **Partner agents marketplace** | Browse and connect to third-party signals, sales, and creative agents |
| **Discovery** | First-class product discovery with persistent `discoveryId` and proposal application |
| **Property lists** | Manage allow/block lists at advertiser scope |
| **Frequency cap & pacing** | Reusable configs at advertiser level, applied to campaigns and media buys |
| **Audit logs / Tasks / Notifications** | Operational visibility for long-running and async operations |

***

## Endpoint mapping

v1 used verb-style RPC paths (`/brand-agent-create`, `/campaign-update`). v2 is fully RESTful with resource-oriented paths under `/api/v2/buyer` and `/api/v2/storefront`.

<Note>
  v2 base URLs (canonical, pinned to v2 — these will keep serving v2 after future major versions):

  * Buyer REST: `https://api.apostra.com/api/v2/buyer`
  * Storefront REST: `https://api.apostra.com/api/v2/storefront`

  The unversioned forms (`/api/buyer`, `/api/storefront`) 308-redirect to whatever major version is currently stable — see [Versioning](/v2/authentication#versioning).

  ADCP property-list resolution lives at the app-level top — `GET /lists/:listId`.
</Note>

### Core resources

| v1 endpoint | v2 endpoint | Notes |
| - | - | - |
| `POST /brand-agent-create` | `POST /advertisers` | Renamed; `brand` field now required |
| `POST /brand-agent-update` | `PUT /advertisers/{advertiserId}` | RESTful update |
| `POST /brand-agent-list` | `GET /advertisers` | Standard list |
| `POST /brand-agent-get` | `GET /advertisers/{advertiserId}` | |
| `POST /brand-agent-delete` | `DELETE /advertisers/{advertiserId}` | |
| `POST /campaign-create` | `POST /campaigns` | New shape: nested `flightDates`, `budget`, `performanceConfig` |
| `POST /campaign-update` | `PUT /campaigns/{campaignId}` | |
| `POST /campaign-list` | `GET /campaigns` | Filter via query params |
| `POST /campaign-get` | `GET /campaigns/{campaignId}` | |
| `POST /campaign-delete` | `DELETE /campaigns/{campaignId}` | |
| `POST /campaign-validate-brief` | (folded into `discover-products`) | Brief validation now happens during discovery |
| `POST /media-buy-create` | (managed via campaigns) | Media buys are created by campaign execution / auto-select |
| `POST /media-buy-execute` | `POST /campaigns/{campaignId}/execute` | Execution is now campaign-scoped |
| `POST /media-buy-list` | `GET /campaigns/{campaignId}/media-buy-status` | Returns status for all media buys on a campaign |
| `POST /signals-list` (v1 partner API) | `GET /signals` (storefront) / via discovery | Signals surface differently per role |
| `POST /creative-create` | `POST /campaigns/{campaignId}/creatives/create` | Creatives are now campaign-nested |
| `POST /creative-update` / `delete` / `get` | `PUT/DELETE/GET /campaigns/{campaignId}/creatives/{creativeId}` | RESTful, manifest-based |
| `POST /creative-assign` | `GET /campaigns/{campaignId}/creativeManifest` | Manifest-driven assignment replaces per-call assign |
| `POST /tactic-*` | (replaced) | Tactics are subsumed by media buys + allocations |
| `POST /outcomes-agent-*` | `/advertisers/{advertiserId}/hypotheses` etc. | Outcome agents become the measurement engine |
| `POST /service-token-*` | (admin UI / unchanged auth model) | Use SSO or API keys; service tokens still work |

### v2-only endpoints (no v1 equivalent)

These exist only on v2 and are central to the new platform:

* `POST /discovery/discover-products` and `/discovery/{discoveryId}/...` — persistent product discovery
* `POST /campaigns/{campaignId}/auto-select-products` — AI-driven product selection and budget allocation
* `GET /audit-logs` — full audit trail
* `GET /tasks/{taskId}` — track async operations
* `GET /notifications` (and unread counts via `help` / `ask_about_capability`)
* `GET /storefronts` / `POST /storefronts` (storefront role) — sell-side surfaces
* `/advertisers/{advertiserId}/measurement-config`, `hypotheses`, `test-plans`, `learning-cycle/run`, `belief-state`, `testability` — measurement engine
* `/advertisers/{advertiserId}/property-lists/...` — allow/block lists
* `/advertisers/{advertiserId}/event-sources` and `/measurement-data/sync` — conversion ingestion
* `/advertisers/{advertiserId}/catalogs` and `/syndicate` — partner-agent catalog distribution
* `/accounts/current`, `/accounts`, `/accounts/create-child` — multi-tenant account hierarchy
* `/notification-preferences` and frequency-cap / pacing-period configs

### AdCP Storefront responses on SDK 14

The REST v2 endpoint mapping above is unchanged, but direct AdCP Storefront
integrations should account for the SDK 14 compatibility projection:

| Previous response | Current response | Migration |
| - | - | - |
| `extensions.scope3.storefront` | `ext.scope3.storefront` | Read custom Storefront metadata from the `ext` namespace. Capability responses temporarily dual-emit the deprecated path while AdCP 3.0 and 3.1 buyers migrate. |
| Top-level `demo` and `demo_disclosure` | `ext.scope3.storefront.demo` and `ext.scope3.storefront.demo_disclosure` | Read both demo markers from the namespaced extension. |
| AdCP 3.1 canonical `format_options[]` | AdCP 3.1 canonical `format_options[]` | No field rename. Select the `format_option_id` from the current discovery result through `format_option_refs`. Seller-authored IDs are stable; synthetic `migrated_…` IDs can change after rediscovery. |
| AdCP 3.0 product formats | Required projected `format_ids[]` | Keep consuming legacy format references when negotiating 3.0. A canonical option must have a valid seller-published mapping; Storefront does not invent one. |
| `sync_creatives` without a selected format | Required `format_id` on each creative | Send the seller-published format reference selected from the product. Capability and task routing report this operation under the `media-buy` protocol. |
| Standard fixed-price pricing models | Standard models plus `revenue_share` | Accept `revenue_share` wherever the AdCP pricing-model enum is consumed; its commercial terms remain seller-defined. |

These changes affect direct AdCP responses, not the camelCase Inventory Feed
authoring contract. See [Create products from ad-server inventory](/v2/storefront/inventory-sources/create-products)
for the authoring-to-buyer projection and [Demo Storefront](/v2/storefront/inventory-sources/demo-storefront)
for disclosure semantics.

<Note>
  **AdCP 3.2 `request_proposals` is implemented but not yet public.** Storefronts
  negotiate AdCP 3.0 and 3.1 today; the compact 3.2 proposal round becomes
  reachable at the 3.2 cutover. When it does, a Storefront answers
  `request_proposals` from the same brief planner as `get_products`: every plan
  returns as a canonical draft proposal whose commercial terms name the
  advertiser (the request's `brand`, or the brand of a natural-key `account`),
  list one purchase per product at the plan's pricing option, and run over the
  flight the buyer stated in `criteria.offer_filters.start_date` and `end_date`
  (or `opportunity.planning_horizon`). A request without an advertiser or a
  flight is refused with `INVALID_REQUEST` naming the missing field; a brief the
  planner cannot turn into a plan returns `outcome: rejected` rather than an
  empty success.

  Storefronts declare `media_buy.outcome_target` and solve a structured
  `criteria.outcome_target` for budget from the plan's published prices. A goal
  is plannable when it names a delivery metric a pricing option is denominated
  in: impressions (CPM), clicks (CPC), views (CPV) or completed views (CPCV).
  For each plan, the purchases priced on that metric buy their share of the
  budget at their fixed price (or, for auction options, the market median, then
  the floor), and the budget that reaches the requested volume is returned as
  `total_budget_guidance` (`min` and `recommended`, no `max`, because a fixed
  price gives one arithmetic answer) and as a `forecast` spend curve at half,
  once and one-and-a-half times that budget whose points carry the goal's
  metric. Each contributing purchase carries the goal in `optimization_goals`
  (impressions are delivery, not an optimisation objective, so they carry
  none). A goal the Storefront cannot plan against (`spend`, an event, a metric
  no option is denominated in, or a brief whose plans are all priced on another
  metric) is refused with `INVALID_REQUEST` naming `criteria.outcome_target.goal`;
  state that goal in the brief instead.
</Note>

***

## Side-by-side: creating a campaign

<Tabs>
  <Tab title="v1 — brand-agent + campaign">
    ```bash theme={null}
    # Step 1: create the brand agent
    curl -X POST "https://api.apostra.com/v1/brand-agent-create" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Acme Corp"
      }'

    # Step 2: create the campaign
    curl -X POST "https://api.apostra.com/v1/campaign-create" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "brandAgentId": "agent_123",
        "name": "Q1 Launch",
        "startDate": "2026-02-05",
        "endDate":   "2026-02-28",
        "budget":    50000
      }'
    ```
  </Tab>

  <Tab title="v2 — advertiser + campaign">
    ```bash theme={null}
    # Step 1: create the advertiser
    curl -X POST "https://api.apostra.com/api/v2/buyer/advertisers" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "name":  "Acme Corp",
        "brand": "acme.com"
      }'

    # Step 2: create the campaign
    curl -X POST "https://api.apostra.com/api/v2/buyer/campaigns" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "advertiserId": 24,
        "name":         "Q1 Launch",
        "flightDates":  {
          "startDate": "2026-02-05T00:00:00Z",
          "endDate":   "2026-02-28T23:59:59Z"
        },
        "budget": {
          "total":    50000,
          "currency": "USD"
        },
        "performanceConfig": {
          "optimizationGoals": [
            { "kind": "metric", "metric": "completed_views" }
          ]
        }
      }'
    ```
  </Tab>
</Tabs>

Notable differences:

* `brandAgentId` (string) → `advertiserId` (number)
* `startDate` / `endDate` flat fields → nested `flightDates` object with ISO 8601 datetimes
* `budget: 50000` → nested `budget` object with explicit `currency`
* New optional `performanceConfig.optimizationGoals[]` for goal-based optimization
* Required `brand` field on advertiser create (e.g., `"acme.com"`)

***

## Authentication changes

The bearer-token model from v1 still works in v2. What's new:

<CardGroup cols={2}>
  <Card title="OAuth for AI agents" icon="key" href="/v2/authentication">
    Connect Claude, ChatGPT, and other MCP-aware agents via OAuth. No API keys to manage.
  </Card>

  <Card title="SSO" icon="lock" href="/v2/setup/sso-setup">
    SAML / OIDC SSO for enterprise org sign-in.
  </Card>

  <Card title="AAO OAuth" icon="store">
    Sign in to private / member storefronts via the AAO marketplace using OAuth.
  </Card>

  <Card title="API keys" icon="bolt" href="/v2/authentication">
    Same `Bearer scope3_...` format as v1 — your existing keys keep working.
  </Card>
</CardGroup>

<Tip>
  If you only use API keys today, no auth changes are required. Just point your existing key at the v2 base URL.
</Tip>

***

## Breaking changes checklist

When you migrate, audit for these:

* [ ] **Path style** — every v1 verb endpoint (e.g. `/campaign-create`) maps to a RESTful resource (e.g. `POST /campaigns`)
* [ ] **`brandAgentId` → `advertiserId`** — and the type changed from string to number
* [ ] **Date fields** — flat `startDate` / `endDate` are now nested under `flightDates` with required ISO 8601 datetimes (with `Z` suffix)
* [ ] **Budget** — scalar `budget: 50000` is now an object with `total` and `currency`
* [ ] **Required `brand`** — advertiser create now requires a brand domain (e.g. `"acme.com"`)
* [ ] **Creatives** — now nested under `/campaigns/{campaignId}/creatives` and use a manifest model rather than per-call assign
* [ ] **Tactics** — removed as a top-level concept; functionality lives in media buys + allocations
* [ ] **Outcome agents** — replaced by the measurement engine (hypotheses, test plans, learning cycles)
* [ ] **MCP tools** — per-domain v1 tools are not available in v2; use `api_call` + `ask_about_capability`
* [ ] **Field naming** — v2 is strict camelCase. `advertiser_id` is rejected; use `advertiserId`. `flight_dates` is rejected; use `flightDates`.
* [ ] **Optimization goals** — now live on `performanceConfig.optimizationGoals[]` with `kind: "event" | "metric"`. Valid `metric` values: `clicks`, `views`, `completed_views`, `viewed_seconds`, `attention_seconds`, `attention_score`, `engagements`, `follows`, `saves`, `profile_visits`, `reach`. `impressions` is a delivery rollup, not a valid optimization target.
* [ ] **Creative assignment** — done via `creativeManifest` rather than per-creative `creative-assign` calls

***

## Skill / MCP migration

If your v1 integration drove the API through per-domain MCP tools, you'll dispatch through the generic v2 tools instead.

<Tabs>
  <Tab title="v1 — per-domain tool">
    ```jsonc theme={null}
    // MCP tool call: brand-agent-create
    {
      "name": "brand-agent-create",
      "arguments": {
        "name": "Acme Corp"
      }
    }
    ```
  </Tab>

  <Tab title="v2 — generic api_call">
    ```jsonc theme={null}
    // MCP tool call: api_call with named operation
    {
      "name": "api_call",
      "arguments": {
        "operation": "create_advertiser",
        "body": {
          "name":  "Acme Corp",
          "brand": "acme.com"
        }
      }
    }
    ```
  </Tab>
</Tabs>

The recommended pattern for any v2 agent:

<Steps>
  <Step title="Discover the operation">
    Call `ask_about_capability` with a natural-language description of what you want to do. The response lists candidate operations, required fields, display requirements, and workflow notes.
  </Step>

  <Step title="Verify every field has a source">
    Walk every field in `body`, `pathParams`, and `params` and name where it came from — a previous response, the user, or `ask_about_capability`. Never invent IDs or fabricate values.
  </Step>

  <Step title="Dispatch via api_call">
    Pass `operation`, `pathParams`, `body`, and `params` to `api_call`. The named `operation` enum prevents hallucinated endpoints and wrong HTTP methods.
  </Step>

  <Step title="Present results per the display rules">
    Each operation's `ask_about_capability` response describes what to show the user (status, IDs, account links, sandbox flag, etc.). Show those fields — never collapse to vague prose.
  </Step>
</Steps>

Use the buyer MCP endpoint and canonical storefront agent URL below:

| Role | URL |
| - | - |
| Buyer | `https://api.apostra.com/mcp/buyer` |
| Storefront | `https://interchange.io` |

***

## Sandbox

v2 has first-class sandbox support. Set `"sandbox": true` when creating an advertiser and every ADCP operation under that advertiser routes to sandbox accounts — no real spend, no production side effects.

<Card title="Sandbox guide" icon="flask" href="/v2/features/sandbox">
  Learn how to test campaigns, discovery, and media-buy flows safely.
</Card>

<Warning>
  The `sandbox` flag is **immutable** after the advertiser is created. Decide up front whether the advertiser is for production or testing.
</Warning>

***

## Getting help

<CardGroup cols={3}>
  <Card title="Quick Start" href="/v2/quickstart" icon="rocket">
    Connect an agent and make your first call in minutes.
  </Card>

  <Card title="API Reference" href="/v2/buyer-api-reference" icon="code">
    Browse every v2 endpoint with request and response schemas.
  </Card>

  <Card title="Authentication" href="/v2/authentication" icon="key">
    OAuth, SSO, and API key setup.
  </Card>

  <Card title="Built for Agents" href="/v2/setup/built-for-agents" icon="robot">
    Detailed setup for Claude, ChatGPT, Cursor, and custom agents.
  </Card>

  <Card title="Skill File" href="/v2/skill" icon="file-code">
    Why a skill file beats hand-written tool wrappers.
  </Card>

  <Card title="Support" href="mailto:support@apostra.com" icon="envelope">
    Email [support@apostra.com](mailto:support@apostra.com) for migration help.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.