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

# Storefront Onboarding

> End-to-end guide for sellers/publishers to onboard their storefront, register inventory, and start accepting agent traffic

<Note title="Beta">
  The v2 API is in active development. The onboarding flow described here may evolve before general availability.
</Note>

## Overview

A **Storefront** is your buyer-facing home on Interchange: the business presence, name, description, and discovery surface buyers use to understand who they are buying from. Your **Merchandising Agent** runs that Storefront, implements the AdCP media-buy workflow, and draws from the inventory sources you connect.

### The first question: how will you sell?

Before anything else, setup asks one question — **are you here to connect a
third-party sales agent you already use, or to sell through Scope3?** Your
answer is recorded on the storefront as `setupIntent` and shapes everything
that follows:

* **`third_party_connect`** — Interchange stays a clean pass-through to your
  external sales agent. Setup never offers ad-server connection steps or
  Storefront-owned merchandising configuration; buyer requests flow through to
  your agent. All capability flags are set off.
* **`sell_through_scope3`** — Interchange merchandises for you: your
  Merchandising Agent composes sellable products from the sources you connect
  (your ad server via an embedded sales agent, feeds, or other sources).
  Product composition is turned on.

The choice is **reversible at any time** and is never a reset: update the
storefront with the other `setupIntent` (or patch `capabilities` directly) and
the matching setup surfaces appear immediately. The only constraint is that a
storefront with ad-server-backed sources cannot declare `third_party_connect`
— an embedded sales agent is not a pass-through; remove that source first.
If you skip the question, nothing is hidden — setup simply asks it before
steering you down either path.

Set it via `PATCH /api/v2/storefront`, the capability-declaration patch:

```json theme={null}
{ "setupIntent": "sell_through_scope3" }
```

An explicit `capabilities` patch in the same request wins over the intent's
preset, so advanced configurations remain expressible.

<Warning>
  `PATCH /api/v2/storefront` is a **true partial patch**: a capability flag you
  leave out keeps its stored value. `PUT /api/v2/storefront` is the aggregate
  update and applies the whole capability object, so a flag you leave out of a
  `PUT` is stored as `false`. Use the `PATCH` when you mean to change some flags
  and leave the rest alone.
</Warning>

### What you declare vs. what buyers get

Capability is two values, and they can legitimately disagree:

* **`configuredCapabilities`** — the flags you declared. This is what you wrote,
  and it is the field to compare against when you want to know whether a save
  would change anything.
* **`capabilities`** — the **effective** projection buyers see. Derived from your
  declaration plus your source topology and approval settings.

The derivation rules, all of them observable on the storefront read:

* **`capabilitiesLocked: true`** — you have ad-server-backed inventory (an
  embedded sales agent), so all three flags are effectively on regardless of
  what you declared. The storefront *is* the agent buyers address, and the
  composition layer is what turns raw inventory into sellable products. A patch
  that would turn any flag off is rejected rather than silently ignored.
* **Product composition off** — the storefront is a clean pass-through to your
  external sources, so no Storefront-owned workflow is advertised: creative
  review and campaign approval are effectively off even if you declared them on.
* **Product composition on (non-adapter)** — `offersCampaignApproval` is derived
  from `mediaBuyApproval`. Your approval setting is the single source of truth
  for whether composed buys queue for review, so declaring the flag does not
  override it.
* **Adapter-routed storefronts** — your declared flags are returned verbatim;
  the adapter owns its own approval mechanism.

This is why a successful save is not a promise that buyer-facing capability
changed. Read `capabilities` back after a write, not just the flags you sent,
and treat `configuredCapabilities` as the record of your own declaration.
`setupIntent` is likewise a record of your answer, never a runtime mode — `null`
means the question has not been asked yet, which is different from declaring
pass-through.

The seller journey has **four user-visible steps**, mirroring the in-app onboarding UI:

1. **Verify your company** — resolve your brand from the AAO registry, set your operator domain, and auto-verify (or fall back to manual KYC).
2. **Connect inventory sources** — register one or more inventory sources: an external sales agent, your own ad server with Interchange-managed sales-agent plumbing, or another Storefront.
3. **Set up settlement and payouts** — confirm the storefront currencies used
   for settlement, then add payout bank details so Interchange can pay you by
   bank transfer. Currency is part of go-live readiness. Payout details are
   required to receive disbursements for normal storefronts, but they never
   block launch: funds accrue until the details are added. They are optional
   for official Scope3 sales-adapter storefronts that already operate under a
   downstream platform settlement agreement. Seller-cleared settlement for
   normal storefronts is coming later and is not configurable today.
4. **Go live** — satisfy every current readiness requirement, including one successful transaction for the compatibility-named `publish_validation` check (normally a seller-owned no-spend sandbox test before first launch). The derived status becomes `live` automatically while the storefront is not paused; Scope3 review remains a separate prerequisite for public buyer discovery.

A few helper endpoints support these steps but are not standalone "steps":

* `POST /resolve-brand` — looks up your brand in the AAO registry. Used inside Step 1 to pre-fill the form.
* `GET /discover-agents` — surfaces agents AAO knows about for your domain. Used inside Step 2.
* `GET /readiness` — the canonical status projection you can call any time to see what gates remain.

<Info title="Where protocol and registry truth lives">
  The AdCP specification and AAO registry define the protocol, registry records,
  storyboards, and validation semantics. This guide explains how Interchange uses
  those signals during setup: AAO **registration** blocks connecting an external
  agent source; AAO **compliance** is surfaced as an advisory warning; publisher
  `adagents.json` **authorization** is surfaced as an advisory setup/product signal
  today; and marketplace listing is a separate Scope3 review step after activation.
</Info>

<Tip>
  Each seller account receives **one storefront automatically** when the account
  is provisioned, and each storefront can connect one or more inventory sources.
  There is no `customerId` path parameter — the storefront is resolved from your
  API key's account context.
</Tip>

### Using the Interchange app

You do not need to create a storefront before starting setup in the Interchange
app; it already exists when your seller account is ready. Open **Business
profile**, choose **Build my profile**, and tell Murph about your business,
inventory, channels, regions, and buyer-facing pitch. Murph will propose the
profile for your confirmation.

The remaining setup areas then guide you through connecting inventory, setting
your selling rules, testing the storefront, and resolving readiness blockers.
Buyer Setup and Seller Setup share the same status, progress, and operator
editing pattern. Seller Setup then adds storefront-only tracks for inventory,
publisher authorization, settlement, and **Get paid**; the last of those stays
visible without being counted as a launch blocker.
Setup time depends on the inventory sources you connect, their authorization
and compliance state, and whether your account is ready for billing and
activation; Interchange does not promise a fixed setup time.
Use the API flow below only when you are integrating programmatically.

### IU plan during the staging pilot

The Organization IU Rate Card is published in staging for a controlled pilot and
visible only to invited organizations. Public seller signup remains closed
until the same `iu-rate-card-pilot` flag is rolled out globally. Invited staging
organizations see the exact Rate Card revision on their next eligible login;
after global rollout, new organizations see it during signup as well. Accepting
it creates an immutable record of the exact revision and plan accepted, but IU
metering, balance drawdown, entitlements, invoices, payment collection, and
renewal charging remain off.

You can instead **Continue without a paid plan** or **Decide later**. Continuing
without a plan suppresses the automatic login prompt only for that exact Rate
Card revision; deciding later allows it to appear again on the next login. The
manual **Choose an IU plan** action remains available under **Settings → Plan &
Billing** while the offer is current. These plan choices are separate from the
payout details required for Interchange-cleared storefront settlement.

## Who this is for

* Publishers and sales houses connecting their inventory to agentic buyers
* Retail media networks exposing on-site or off-site inventory through AdCP-compatible agents
* Any seller who wants buyer agents (e.g. Scope3, Claude, custom buyers) to be able to discover and transact against their inventory

## Prerequisites

<Steps>
  <Step title="Interchange API key">
    Generate a key at [interchange.io/user-api-keys](https://interchange.io/user-api-keys). Keys start with `scope3_` and authorize all storefront endpoints.
  </Step>

  <Step title="A registered brand on AAO">
    Your brand should have a `brand.json` published and resolvable through the AAO registry at `agenticadvertising.org`. If you don't have one yet, the resolve-brand call returns a `builderUrl` that points you to the registry's brand builder.
  </Step>

  <Step title="At least one inventory source">
    An external AdCP-compatible sales agent, an operator-owned ad server, or another Storefront. For external agents, you'll need the endpoint URL, protocol, and (for non-OAuth agents) auth credentials.
  </Step>

  <Step title="Optional: payout bank details">
    Required for Interchange-cleared settlement. Have your bank details ready — beneficiary name and address, account number or IBAN, one bank identifier (Fedwire/ABA routing number, CHIPS ABA, SWIFT-BIC, or local bank code), and the currency your account accepts. Accounts under an organization inherit billing from the organization and do not set up their own. Seller-cleared settlement for normal storefronts is not configurable yet.
  </Step>
</Steps>

***

## Onboarding flow

<Steps>
  <Step title="Verify your company">
    The first thing a seller does is identify their company so Scope3 can pull their brand profile from AAO and validate the operator domain. This step combines a brand lookup, a storefront update, and an automatic operator-domain verification check.

    #### 1. Resolve your brand (helper)

    Look up your brand in the AAO registry to grab the canonical brand name and logo URL. This call has no side effects — it's only used to populate the storefront update payload.

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.interchange.io/api/v2/storefront/resolve-brand \
        -H "Authorization: Bearer scope3_..." \
        -H "Content-Type: application/json" \
        -d '{ "domain": "acme.com" }'
      ```
    </CodeGroup>

    ```json Response (resolved) theme={null}
    {
      "resolved": true,
      "domain": "acme.com",
      "brandName": "Acme",
      "logoUrl": "https://cdn.example.com/acme-logo.svg",
      "manifestUrl": "https://acme.com/.well-known/brand.json",
      "manifest": { "...": "full brand.json" },
      "registryEntry": { "...": "AAO registry entry" },
      "authorizedOperators": [
        { "domain": "acme-media.com", "scope": "primary" }
      ],
      "houseBrand": false
    }
    ```

    If no manifest is found, the call returns `200` with `{ "resolved": false, "builderUrl": "https://agenticadvertising.org/brand" }`.

    **What if my brand isn't found?** A `resolved: false` is not an error and does not block you — it just means you haven't published a `brand.json` yet. The storefront shows no resolved brand logo in that state; it never substitutes a logo inferred from your website or a third-party enrichment service. Publish one at the `builderUrl` (or host your own at `/.well-known/brand.json`) and re-run `resolve-brand`; we read it live. Your `brand.json` is your own identity document — we read it, we never own it. See [Identity documents](/v2/concepts/identity-documents) for what it declares and how it differs from publisher authorization (`adagents.json`).

    <Note>
      The `domain` field is validated against a strict FQDN regex. IP addresses and internal hostnames are rejected to prevent SSRF.
    </Note>

    #### 2. Write the brand fields onto your storefront

    Seller account provisioning creates this storefront record automatically. You
    can retrieve it with `GET /storefront`. `POST /storefront` remains idempotent for
    programmatic recovery and returns the existing record rather than creating a
    duplicate:

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.interchange.io/api/v2/storefront \
        -H "Authorization: Bearer scope3_..." \
        -H "Content-Type: application/json" \
        -d '{
          "name": "Acme Media",
          "publisherDomain": "acme.com",
          "operatorDomain": "acme.com",
          "plan": "basic"
        }'
      ```
    </CodeGroup>

    `POST /storefront` is idempotent — if a storefront already exists for your account, the existing record is returned instead of creating a duplicate.

    `operatorDomain` is the canonical domain this storefront operates as and the identity buyers and AAO matching use for the storefront. It can differ from the account's registered `customerDomain`, and it can be left unset during setup if the operator is not known yet. A storefront cannot go live until an operator domain is set and verified. `publisherDomain` is optional storefront metadata and should not be used as the matching key for cross-publisher storefronts.

    Then update it with the brand fields from `resolve-brand` plus your operator domain:

    <CodeGroup>
      ```bash curl theme={null}
      curl -X PUT https://api.interchange.io/api/v2/storefront \
        -H "Authorization: Bearer scope3_..." \
        -H "Content-Type: application/json" \
        -d '{
          "operatorDomain": "acme.com",
          "brandName": "Acme",
          "logoUrl": "https://cdn.example.com/acme-logo.svg"
        }'
      ```
    </CodeGroup>

    ```json Response theme={null}
    {
      "platformId": "acme-media",
      "name": "Acme Media",
      "publisherDomain": "acme.com",
      "operatorDomain": "acme.com",
      "brandName": "Acme",
      "logoUrl": "https://cdn.example.com/acme-logo.svg",
      "operatorDomainVerified": true,
      "plan": "basic",
      "status": "PENDING",
      "createdAt": "2026-04-25T12:00:00.000Z",
      "updatedAt": "2026-04-25T12:00:00.000Z"
    }
    ```

    <Tip title="Operator domain auto-verification">
      When you set `operatorDomain`, the API first checks for an exact match against an approved `customerDomain`. If it does not exactly match, Interchange checks the alias/rebrand evidence path below. A Scope3 verification request is needed only when neither path verifies the operator domain automatically.

      If your storefront operates under an alias or rebrand domain, such as an `operatorDomain` that differs from your team's email domain, keep the canonical operator domain you want buyers to see. Interchange can auto-verify the alias when your registered account domain is already approved and trusted AAO or `brand.json` evidence connects the account domain and requested operator domain to the same organization or ownership chain. Useful evidence includes AAO registry org linkage, `brand.json` `authoritative_location`/house portfolio entries, and `authorized_operators`. AAO TXT records, `/.well-known/adagents.json`, AAO Partner membership, and website redirects help Scope3 review the request, but a redirect is not ownership proof by itself.

      **Your account domain is approved first, and its approval gates everything else.** Approve it by activating a member whose email matches that domain, or by requesting Scope3 attestation for it. Until the account domain is approved, alias and rebrand evidence is not evaluated at all — so an unapproved account domain, rather than your published evidence, is a common reason an alias operator domain stays pending.

      The `operator_domain` readiness check names which case applies and who acts next:

      | Readiness reports                                 | Who acts | What to do                                                                           |
      | ------------------------------------------------- | -------- | ------------------------------------------------------------------------------------ |
      | The account has no registered domain              | you      | add the account domain, then get it approved                                         |
      | The account domain is not yet approved            | you      | activate a matching member email, or request Scope3 attestation                      |
      | No AAO or `brand.json` evidence links the domains | you      | publish or correct that evidence                                                     |
      | Scope3 could not complete the evidence check      | Scope3   | nothing — the lookup failed or timed out, and it retries on the next storefront read |

      The last row is not a finding about your evidence: your published evidence may already be correct, and there is nothing for you to fix.

      The account `customerDomain` and storefront `operatorDomain` are intentionally separate fields. Updating `customerDomain` syncs the storefront operator domain only when the storefront has no operator domain yet or is still mirroring the previous account domain **and** has no populated operator-identity profile. If the storefront has an explicitly different operator domain, or changing a mirrored domain would clear its `description`, `channels`, `membershipStatus`, or `website`, the API preserves the storefront domain. Change it directly with `PUT /storefront`, where you can resupply the new operator's profile or explicitly confirm the reset.
    </Tip>

    <Warning title="Changing an operator domain can reset its old identity profile">
      `description`, `channels`, `membershipStatus`, and `website` describe the
      current operator identity. When `operatorDomain` changes, the API refuses to
      silently clear any populated values among those fields. The validation response
      lists the affected fields.

      Either resupply valid values for the new operator in the same `PUT`, or pass
      `"confirmOperatorDomainProfileReset": true` to clear the unprovided values.
      Explicitly resupplied fields are preserved or replaced; an unchanged
      `operatorDomain` does not reset anything and needs no confirmation.
    </Warning>

    Common identity fields on `PUT /storefront` include `name`, `publisherDomain`,
    `operatorDomain`, `brandName`, `logoUrl`, `logoBackground`, `description`,
    `channels`, `membershipStatus`, and `website`. The endpoint also accepts the
    storefront configuration fields in the API reference. Confirmation fields alone
    do not count as an update; at least one mutation field must be provided.
  </Step>

  <Step title="Connect inventory sources">
    An **inventory source** connects a named slot inside your Storefront to something your Merchandising Agent can draw from: an external ADCP-compatible sales agent, an operator-owned ad server with Interchange-managed sales-agent plumbing behind it, or another Storefront. Buyer-side discovery surfaces your Merchandising Agent as the ADCP actor for the Storefront, and discovery or media-buy calls route through the active source behind it.

    #### 1. Discover agents (helper)

    Optional but recommended: see what AAO already knows about your domain. This proxies AAO's operator and publisher endpoints plus your `.well-known/adagents.json`.

    <CodeGroup>
      ```bash curl theme={null}
      curl "https://api.interchange.io/api/v2/storefront/discover-agents?domain=acme.com" \
        -H "Authorization: Bearer scope3_..." \
        -H "x-aao-api-key: aao_..."
      ```
    </CodeGroup>

    The `x-aao-api-key` header is optional. Without it you only get the public registry view. Pass it to also surface storyboard compliance status for agents you operate.

    ```json Response theme={null}
    {
      "domain": "acme.com",
      "operator": {
        "domain": "acme.com",
        "member": { "slug": "acme-media", "display_name": "Acme Media" },
        "agents": [
          {
            "url": "https://agent.acme-media.com/mcp",
            "name": "Acme Media Sales",
            "type": "SALES",
            "compliance": {
              "status": "passing",
              "storyboards_passing": 12,
              "storyboards_total": 12,
              "headline": "All scenarios pass"
            }
          }
        ]
      },
      "publisher": {
        "domain": "acme.com",
        "adagents_valid": true,
        "properties": [ { "id": "acme-app", "type": "mobile_app", "name": "Acme App" } ],
        "authorized_agents": [
          { "url": "https://agent.acme-media.com/mcp", "authorized_for": ["display"] }
        ]
      }
    }
    ```

    Responses are cached server-side for 2 minutes per `(domain, key fingerprint)`. Pass `&refresh=true` to force a re-fetch.

    #### 2. Register an inventory source

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.interchange.io/api/v2/storefront/inventory-sources \
        -H "Authorization: Bearer scope3_..." \
        -H "Content-Type: application/json" \
        -d '{
          "sourceId": "acme-sales",
          "name": "Acme Media Sales",
          "executionType": "AGENT",
          "type": "SALES",
          "endpointUrl": "https://agent.acme-media.com/mcp",
          "protocol": "MCP",
          "authenticationType": "API_KEY",
          "auth": { "type": "bearer", "token": "agent_abc123..." },
          "description": "Primary external sales agent for Acme on-site display + CTV"
        }'
      ```
    </CodeGroup>

    **Required when `executionType: "agent"`:** `type`, `endpointUrl`, `protocol`, `authenticationType`. **`auth` is required** for `API_KEY`, `JWT`, and `BASIC_AUTH` agents and **must be omitted** for `OAUTH` and `NO_AUTH`.

    <Warning title="How auth credentials are stored">
      Inventory-source credentials (API keys, Basic usernames/passwords, and JWT private keys) are encrypted at rest and only referenced by an opaque `auth_secret_ref` in the database. They are **never echoed back** in API responses (the response surfaces `authConfigured: true` instead). Never log, screenshot, or commit raw credentials to source control. Rotate immediately if a credential is exposed.
    </Warning>

    <Tabs>
      <Tab title="API_KEY">
        ```json theme={null}
        {
          "authenticationType": "API_KEY",
          "auth": { "type": "bearer", "token": "agent_abc123..." }
        }
        ```

        Token formats `bearer`, `apikey`, and `api_key` are all accepted. The token is encrypted at rest and never echoed back. The source goes to `pending` and auto-activates once its credential is configured. Reachability is reported independently by source health and connectivity checks.
      </Tab>

      <Tab title="OAUTH">
        ```json theme={null}
        { "authenticationType": "OAUTH" }
        ```

        Omit the `auth` field entirely. The response includes an `oauth.authorizationUrl` you must redirect the operator to. Auth completes out-of-band; the source flips to `active` once the OAuth callback succeeds.

        <Warning>
          The OAuth `state` parameter Scope3 attaches to the authorization URL is opaque, single-use, and carries CSRF entropy scoped to your account + source. Do **not** decode, modify, or replay it — the callback handler validates `state` server-side and rejects mismatches. If you proxy the redirect through your own server, forward `state` byte-for-byte.
        </Warning>
      </Tab>

      <Tab title="BASIC_AUTH">
        ```json theme={null}
        {
          "authenticationType": "BASIC_AUTH",
          "auth": {
            "type": "basic",
            "username": "agent-user",
            "password": "agent-password"
          }
        }
        ```

        Username and password are encoded into the outbound HTTP `Authorization: Basic ...` header when Scope3 calls the source. The raw credentials are encrypted at rest and never echoed back.
      </Tab>

      <Tab title="JWT">
        ```json theme={null}
        {
          "authenticationType": "JWT",
          "auth": {
            "type": "jwt",
            "privateKey": "-----BEGIN PRIVATE KEY-----\n...",
            "issuer": "https://acme.com",
            "subject": "acme-sales",
            "keyId": "key-1",
            "scope": "agent:invoke",
            "tokenEndpointUrl": "https://auth.acme.com/oauth/token",
            "audienceUrl": "https://agent.acme-media.com/mcp"
          }
        }
        ```

        <Warning>
          JWT private keys are long-lived signing credentials. Treat them like passwords — never log them, never commit them to source control, and rotate immediately if exposed. Submitted material is encrypted at rest and never echoed back.
        </Warning>
      </Tab>

      <Tab title="NO_AUTH">
        ```json theme={null}
        { "authenticationType": "NO_AUTH" }
        ```

        Public agents only. The source is created with status `ACTIVE` immediately.
      </Tab>
    </Tabs>

    ```json Response theme={null}
    {
      "sourceId": "acme-sales",
      "name": "Acme Media Sales",
      "executionType": "AGENT",
      "status": "PENDING",
      "agentId": "agent_01HX...",
      "type": "SALES",
      "endpointUrl": "https://agent.acme-media.com/mcp",
      "protocol": "MCP",
      "authenticationType": "API_KEY",
      "authConfigured": true,
      "createdAt": "2026-04-25T12:05:00.000Z",
      "updatedAt": "2026-04-25T12:05:00.000Z"
    }
    ```

    <Warning title="AAO registry checks (connect vs. readiness)">
      AAO is checked at **connect time** and by the readiness projection, but the only hard AAO gate is connect-time **registration**. Connect-time requires that the agent is **registered** with AAO; failing or pending agents can still be connected. In readiness, AAO compliance is **informational** — a non-`passing` verdict is surfaced as a prominent warning but does not block transactions.

      | AAO status       | Connect a source                                                 | Transaction readiness                |
      | ---------------- | ---------------------------------------------------------------- | ------------------------------------ |
      | `passing`        | OK                                                               | OK                                   |
      | `pending`        | OK (logged)                                                      | OK — surfaced as an advisory warning |
      | `not-passing`    | OK (logged)                                                      | OK — surfaced as an advisory warning |
      | `not-registered` | **Rejected** with `VALIDATION_ERROR`                             | n/a                                  |
      | AAO unreachable  | **Rejected** with `SERVICE_UNAVAILABLE` — retry once it recovers | n/a                                  |

      The source still must be operational — the compatibility-named `agent_status` readiness check (see Step 4) reads the inventory source's canonical lifecycle and is a blocker for third-party sources. The connection sidecar and legacy `adcp_agent.status` value are not separate storefront lifecycle signals. AAO compliance (the `agent_connectivity` readiness check) is advisory: a non-`passing` verdict surfaces for review and does not leave the storefront `blocked`. Determining who may sell which publisher inventory stays a per-publisher authorization question against the publisher's own `adagents.json`, not this compliance score. The connect-time AAO registry gate runs uniformly for every caller — there is no SuperAdmin bypass.
    </Warning>

    Storefronts can connect as many external `AGENT` inventory sources as needed — no per-plan cap is enforced today. Ad-server-backed inventory sources (`MANAGED_SALES_AGENT`) are slot-exempt regardless of plan.

    Updates are partial — `PUT /api/v2/storefront/inventory-sources/:sourceId` accepts any subset of `name`, `description`, `endpointUrl`, `protocol`, `authenticationType`, `auth`, `status`. Updating `auth` rotates the stored credential; omitting it preserves the existing one.

    #### 3. How buyers discover this storefront

    Once a Storefront is transacting, buyer discovery can surface its Merchandising Agent as a first-class ADCP sales agent with ID `storefront-{platform_id}`, where `platform_id` is the Storefront's public `platformId` slug. Buyers call the Storefront surface; they do not target inventory source IDs directly in buyer discovery.

    `get_products` behavior depends on the storefront mode:

    * **Composition** — the Merchandising Agent composes buyer-facing products from active ingredient sources plus the active operating instructions.
    * **Passthrough** — the Merchandising Agent proxies `get_products` to an active source and returns the upstream products with Storefront identity overlaid.

    Matching buyer instructions are resolved at `get_products` time using operator domain, brand domain, and optional country. Composition storefronts apply them during composition; passthrough storefronts apply them as response overlays, including discounts and notes. Without at least one active source, buyer `get_products` has nothing to compose from or proxy to.

    <Note>
      Execution-specific configuration (per integration knobs — e.g. ad-server adapter settings for Interchange-managed ad-server sources) lives in typed fields per integration type, not in a generic config bag. New integration types add their own typed fields rather than overloading a polymorphic blob.
    </Note>

    <Note title="Ad-server sources — supported adapters">
      Ad-server-backed inventory sources (`executionType: "MANAGED_SALES_AGENT"`) wire your Storefront to one of four operator-owned ad-server adapters. Interchange manages the AdCP plumbing behind your ad server. Pick one when creating the connection via `POST /api/v2/storefront/esa`:

      | Adapter                                     | Credentials you supply                                                                                                            | How Scope3 handles them                                                                                                                                                                                                                                         |
      | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
      | **Google Ad Manager** (`google_ad_manager`) | Numeric network code.                                                                                                             | Scope3 provisions a service account dedicated to your account; you grant it access in your GAM admin console.                                                                                                                                                   |
      | **SpringServe** (`springserve`)             | Login email + password, or a pre-minted API token. New setup pins `springserve:v1` with the matching typed authentication method. | Forwarded to the managed ad-server source and encrypted there; only non-secret display configuration and the versioned contract selection stay on the Interchange connection row. The source mints a fresh 2-hour token from email/password and auto-refreshes. |
      | **FreeWheel** (`freewheel`)                 | Publisher API client ID + client secret. A 7-day temporary access key is also accepted for advanced testing.                      | Forwarded to the managed ad-server source and encrypted there; never written to the Interchange connection row. The client ID/secret path mints and auto-refreshes short-lived tokens.                                                                          |
      | **AdsWizz** (`adswizz`)                     | Static API key, numeric agency id, and three-letter agency billing currency.                                                      | New setup pins `adswizz:v1` with fixed Domain/Forecasting endpoints and `x-api-key` authentication. The key is encrypted by the managed source and never written to the Interchange connection row.                                                             |

      Only non-secret display fields (login, environment, default advertiser/demand-partner id) are stored on the connection row so the UI can render "connected as ...". Rotating credentials on a live ad-server source preserves products, principals, and sync history via `PUT /api/v2/storefront/inventory-sources/{sourceId}/adapter-config`.
    </Note>

    <Note title="Adapter-routed storefronts — supported providers">
      Some storefronts route directly to a wired provider adapter rather than an
      inventory-source sales agent. Supported adapter provider values are `amazon`,
      `audiostack`, `google`, `meta`, `pinterest`, `reddit`, `snap`, `spotify`, and
      `tiktok`.

      Delegated OAuth for these adapter storefronts uses the shared adapter callback
      path:

      ```text theme={null}
      https://api.interchange.io/oauth/adapters/{provider}/callback
      ```

      Register the same path on staging when testing, for example
      `https://api.staging.interchange.io/oauth/adapters/snap/callback`.

      Reddit access tokens expire after one hour. Interchange requests permanent
      authorization and uses Reddit's refresh token to renew access automatically. If
      an existing Reddit connection reports expired credentials after one hour,
      reconnect it once in **Settings → Connections**; new and reconnected grants
      then refresh automatically.
    </Note>

    <Tip title="Credential handoff and testing">
      For FreeWheel and SpringServe, prefer the storefront credential screen for
      passwords and tokens rather than pasting secrets into chat. Murph can send the
      operator directly to the secure form with
      `/{accountId}/storefront?tab=sources&connectAdServer=freewheel` or
      `/{accountId}/storefront?tab=sources&connectAdServer=springserve`. That link
      opens Inventory sources, launches Connect ad server, and preselects the right
      adapter. Murph can then wait for submission, list the ad-server sources to
      find the new or updated connection, and run
      `POST /api/v2/storefront/esa/{esaId}/test-connection` to verify that the
      upstream source can authenticate.

      For testing, use a temporary API token when the ad server supports one. For
      production, the credential pair is usually better because the upstream source can
      mint and refresh short-lived tokens automatically. In either case, Scope3 stores
      only non-secret display fields; the upstream source holds the encrypted
      secret.
    </Tip>

    ### Uploading setup documents to Murph

    Murph can use uploaded PDFs, decks, spreadsheets, images, and text documents
    during storefront setup. Use this for brand books, media kits, operating instructions,
    rate cards, do-not-air lists, and other materials that would otherwise need to
    be pasted into chat.

    Uploaded documents are summarized instead of copied back verbatim. The
    document-processing status includes:

    | Field                    | Type           | Description                                                                                                                                                                                                                |
    | ------------------------ | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `extractedFacts`         | array          | Structured facts extracted from the uploaded files. Each fact includes `type`, `content`, `confidence`, `entities`, and `tags` so Murph can reference the source material in later turns without re-reading the full file. |
    | `brandManifestCandidate` | object \| null | Present when a brand book or visual identity document includes fields that may map into AAO `brand.json`. Includes `status`, `confidence`, `rationale`, `mappedFields`, `missingInputs`, and `recommendedActions`.         |

    For brand books, Murph can identify brand.json candidates such as name,
    website URL, colors, fonts, tone, tagline, contact details, and disclaimers.
    Logo images can be uploaded to AAO for review; pending uploads are not public
    and are not written into `brand.json` until AAO approves and lists the
    `/assets/brands/...` URL. Other assets still need public hosted URLs before
    they can be used in `brand.json`.

    <Note>
      Murph can draft and preview `brand.json` fields from an uploaded brand book,
      then compare those fields against the current AAO brand.json state and publish
      the confirmed manifest to AAO for your verified storefront operator domain.
      Uploaded logo images can be sent to AAO review from Murph; only approved AAO
      asset URLs or other public HTTPS URLs are written as logo or asset entries.
    </Note>

    #### Google Ad Manager service-account grant

    Google Ad Manager does not require the publisher to paste a password or API
    token into Scope3. Scope3 creates a service account dedicated to your account
    and returns its email address from `POST /api/v2/storefront/esa/service-account`. The
    publisher grants that service-account email access inside their GAM network,
    then Scope3 provisions the ad-server-backed source with the publisher's numeric
    network code.

    The operator-owned part of the flow is:

    1. Call `POST /api/v2/storefront/esa/service-account` and copy the returned
       `serviceAccountEmail`.
    2. In Google Ad Manager, go to **Admin → Global settings → Network settings →
       Add a service account user**.
    3. Enter the service-account email returned by Scope3.
    4. Grant a role that can read inventory and traffic campaigns, such as
       `Trafficker` or a least-privilege custom role with equivalent API
       permissions.
    5. Wait a few minutes for the grant to propagate.
    6. Create the ad-server source with `POST /api/v2/storefront/esa` and body
       `{ "type": "google_ad_manager", "networkCode": "12345678" }`.

    If the probe returns `ADAPTER_PERMISSION_DENIED`, verify that the exact
    service-account email was added and wait a minute or two before retrying. If it
    returns `ADAPTER_NETWORK_NOT_FOUND`, the network code is likely wrong.

    #### GAM buyer-routing default advertiser

    For Google Ad Manager managed-sales-agent sources, Storefront can clear the
    `Default GAM advertiser` setup blocker through the API. List cached advertiser
    records with `GET /api/v2/storefront/esa/{esaId}/gam/advertisers`, or create
    or find the intended catch-all advertiser with
    `POST /api/v2/storefront/esa/{esaId}/gam/advertisers/ensure`. Then set the
    tenant default with `PUT /api/v2/storefront/esa/{esaId}/gam/default-advertiser`
    using the returned `advertiser.id`.

    This flow configures the upstream sales-agent tenant directly. Operators do not
    need to open the embedded sales-agent UI to set the default GAM advertiser.

    Keep detailed GAM UI wording anchored to Google's own support documentation;
    Scope3 docs should describe the contract we own, the service-account email we
    return, and the role/permission requirements we need.

    <Note>
      Sources can't be deleted while their backing agent has non-terminal media buys (`ACTIVE`, `PAUSED`, `PENDING_APPROVAL`, or `INPUT_REQUIRED`). Cancel or terminate those first.
    </Note>
  </Step>

  <Step title="Set up billing (conditional)">
    Payout details on file let Interchange settle payments on your behalf: Interchange collects from the buyer, deducts the configured fees, and pays you by bank transfer in your payout currency. Whether billing is required depends on the storefront execution path:

    * **Optional** only for an official Scope3 sales-adapter storefront on an existing downstream platform settlement agreement. A third-party sales agent or finished-product/pass-through source does not qualify.
    * **Required to get paid** for every normal storefront, but never required to go live. Interchange clears every normal-storefront buy today; without payout details, funds still accrue against each booking, but Interchange has no way to disburse them. The readiness check `billing_setup` returns `isBlocker: false` in every state — it is advisory, not a go-live gate. An active source that explicitly lacks `agent` billing support still blocks readiness (a different check, `interchange_billing_support`).

    <Warning>
      If billing is **optional** and you skip it, only official sales-adapter buys on the existing downstream platform agreement can operate. Interchange does not clear those media payments. Seller-cleared settlement is not yet configurable for normal storefronts.
    </Warning>

    Accounts under an organization inherit billing from the organization by default. A child-account administrator cannot set up or change a payout destination; a parent administrator can create a child-specific destination for a direct seller storefront.

    #### 1. Save payout details

    Enter your bank details in **Plan & Billing → Payouts**, or via the API:

    <CodeGroup>
      ```bash curl theme={null}
      curl -X PUT https://api.interchange.io/api/v2/storefront/billing/payout-details \
        -H "Authorization: Bearer scope3_..." \
        -H "Content-Type: application/json" \
        -d '{
          "beneficiaryName": "Meridian Media Group Inc.",
          "addressLine1": "500 Harbor Blvd",
          "city": "Seattle",
          "region": "WA",
          "postalCode": "98101",
          "countryCode": "US",
          "accountNumber": "000123456789",
          "bankIdentifierType": "FEDWIRE_ABA",
          "bankIdentifierValue": "021000021",
          "currency": "USD"
        }'
      ```
    </CodeGroup>

    `accountNumber` takes a bank account number or an IBAN; `bankIdentifierType` is one of `FEDWIRE_ABA`, `CHIPS_ABA`, `SWIFT_BIC`, or `BANK_CODE`, with the identifier itself in `bankIdentifierValue`. The account number is encrypted at the application layer before storage and used only to execute payouts; the account number is write-only and never displayed after save. See [Set payout details](/v2/storefront/billing/tasks/set-payout-details) for the full contract.

    #### 2. Confirm what's on file

    <CodeGroup>
      ```bash curl theme={null}
      curl https://api.interchange.io/api/v2/storefront/billing \
        -H "Authorization: Bearer scope3_..."
      ```
    </CodeGroup>

    ```json Response theme={null}
    {
      "billing": {
        "onboardingStatus": "complete",
        "platformFeePercent": 12.5,
        "currency": "USD",
        "defaultNetDays": 30,
        "payoutDetails": {
          "beneficiaryName": "Meridian Media Group Inc.",
          "accountNumberLast4": "6789",
          "bankIdentifierType": "FEDWIRE_ABA",
          "bankIdentifierValue": "021000021",
          "completedAt": "2026-07-01T09:30:00Z"
        },
        "inherited": false
      }
    }
    ```

    #### Other billing endpoints

    | Endpoint                                  | Purpose                                                       |
    | ----------------------------------------- | ------------------------------------------------------------- |
    | `GET /api/v2/storefront/billing`          | Fees, currency, net days, masked payout details               |
    | `PUT /api/v2/storefront/billing`          | Admin-only — update fee config                                |
    | `GET /api/v2/storefront/billing/accounts` | Organizations — billing status across the accounts under them |

    <Note>
      Organization admins can pass `?targetCustomerId=<accountId>` on billing endpoints to operate on a direct seller storefront's billing. Access is validated against the organization/account relationship before each call.
    </Note>

    <Note title="Previously connected Stripe?">
      Interchange no longer uses Stripe for storefront payouts. Enter your bank details once (above) to keep Interchange-cleared settlement on your media buys — details held by Stripe cannot be migrated.
    </Note>
  </Step>

  <Step title="Go live">
    A storefront has no stored `PENDING` or `ACTIVE` lifecycle state. Its effective
    status is always derived from `isPaused`, archive state, and the current
    readiness checks. Buyer agents can transact only when that projection is
    `live`. Public marketplace discovery has one additional human-review gate:
    transaction-ready Storefronts remain pending marketplace review until a Scope3
    admin lists them.

    #### 1. Confirm readiness

    `GET /api/v2/storefront/readiness` computes every current requirement and the effective status. Call it any time during onboarding or operation to see what's missing.

    <CodeGroup>
      ```bash curl theme={null}
      curl https://api.interchange.io/api/v2/storefront/readiness \
        -H "Authorization: Bearer scope3_..."
      ```
    </CodeGroup>

    ```json Response theme={null}
    {
      "platformId": "acme-media",
      "status": "ready",
      "checks": [
        {
          "id": "inventory_sources",
          "name": "Inventory sources",
          "category": "inventory",
          "description": "At least one source has been configured.",
          "status": "complete",
          "isBlocker": true,
          "method": "agent",
          "details": "1 source configured"
        },
        {
          "id": "agent_status",
          "name": "Inventory source status",
          "category": "agents",
          "description": "All inventory sources are active.",
          "status": "complete",
          "isBlocker": true,
          "details": "1 of 1 inventory sources are active"
        },
        {
          "id": "agent_auth",
          "name": "Agent authentication",
          "category": "agents",
          "description": "All agents have been authenticated.",
          "status": "complete",
          "isBlocker": true,
          "details": "1 of 1 agents are authenticated"
        },
        {
          "id": "billing_setup",
          "name": "Billing",
          "category": "billing",
          "description": "Payout details are on file.",
          "status": "complete",
          "isBlocker": false,
          "details": "Payout details added"
        }
      ]
    }
    ```

    Every check also carries a `requirement` classification so you always know what a checklist item actually demands of you:

    * **`hard`** — must be resolved before the storefront can go live. Matches `isBlocker: true`.
    * **`soft`** — advisory: improves outcomes but never blocks.
    * **`platform_default`** — the platform applied a sensible default on your behalf; the `appliedDefault` field states the value in plain terms and how to change it. These are visibility items, never tasks.

    The classification is path-aware: which checks appear, and whether each blocks, depends on how you sell (the `setupIntent` answer, your source types, and whether composition is on) — a pass-through storefront is never asked to complete merchandising items, and vice versa.

    <Accordion title="What each check means">
      * **`publish_validation`** *(blocker; compatibility id)* — one successful transaction must have completed the storefront forwarding path. Either a seller-owned no-spend sandbox test or a successful live buyer media buy satisfies it permanently; a later failed test does not erase prior proof. Before first live traffic, the sandbox path is admitted only for the authenticated seller and does not make the storefront public or change its derived status to `live`. The current harness does not create or validate a creative, so creative readiness remains a separate check when your setup requires it. Run the test by asking Murph, or use the two-step API flow below (planning without execution does not satisfy the check). The check is scoped to your connected source mix: if every connected source is your Scope3-managed sales agent, it reports `complete` ("Covered by your managed sales agent") without a separate test — the managed path is Scope3-operated, so there's nothing unproven to prove. If you also connect a third-party sales agent (or a modular source whose recipe executes campaigns, such as CitrusAd), the check still applies and its description names which source(s) the test still needs to cover.
      * **`publisher_domains`** *(blocker)* — every storefront declares at least one publisher domain so buyers know what inventory is being sold. The operator domain identifies the company operating the storefront and may be different. `adagents.json` authorization is shown separately as the advisory `publisher_authorization` check and never blocks transactions.
      * **`product_publisher_domains`** *(advisory during rollout)* — every active product should map to one of the storefront's declared publisher domains. Missing mappings and undeclared domains warn while older catalogs are backfilled; `adagents.json` authorization is separate and also advisory.
      * **`selling_intent`** *(blocker)* — the first question: connect a third-party sales agent, or sell through Scope3 (`setupIntent`). Complete once declared — or inferred automatically from your configuration (a connected ad-server source or enabled composition implies sell-through; a connected external agent implies pass-through), so existing storefronts are never blocked retroactively. Adapter storefronts skip it.
      * **`approval_settings`** *(platform default)* — how buyer submissions are handled on Interchange-managed sources. Never a task; `appliedDefault` names the current posture and how to change it.
      * **`inventory_sources`** *(blocker)* — at least one source must be connected. Buyer-facing `get_products` still requires at least one **active** source: composition storefronts need active ingredient sources, and passthrough storefronts need an active source to proxy.
      * **`agent_status`** *(blocker; compatibility id)* — every non-disabled external-agent inventory source must have canonical source status `ACTIVE`. No copied sidecar or legacy agent status can independently block it. This gates go-live; AAO compliance does **not** (see `agent_connectivity`).
      * **`agent_auth`** *(blocker)* — non-OAuth agents must have a stored credential. OAuth agents are excluded once their token is captured.
      * **`agent_connectivity`** *(informational, surfaced on `GET /readiness/compliance`)* — reads each agent's AAO compliance verdict and returns per-agent track results and observations. A non-`passing` verdict surfaces as a prominent warning but does **not** block going live. See [Identity documents](/v2/concepts/identity-documents#blocks-vs-informs).
      * **`billing_setup`** *(advisory, never a go-live blocker)* — payout details are **optional** only for the official Scope3 sales-adapter compatibility path; the check returns `status: optional` with `isBlocker: false` and the external-agreements warning. They are **required to get paid** for every normal storefront, including third-party sales-agent and finished-product/pass-through sources — but not required to go live: the check returns `status: missing` with `isBlocker: false` until payout details are on file, and the storefront can activate and transact in the meantime. Funds accrue against every Interchange-cleared booking either way; Interchange just can't disburse them until payout details are added. In **non-production** environments the check short-circuits to `optional` regardless of billing state so dev storefronts can go live without entering real bank details.
    </Accordion>

    Top-level `status` is `blocked` if any check with `isBlocker: true` is not `complete`, otherwise `ready`. A check with `status: optional` is treated as not required.

    #### Programmatic test-campaign gate

    API and agent integrations use the same sandbox/no-spend test core as Murph.
    Start with an exploratory plan to inspect source readiness and selectable
    products. When you know the exact product selection, call plan again with the
    complete execution payload:

    ```bash curl theme={null}
    curl -X POST https://api.interchange.io/api/v2/storefront/test-campaigns/plan \
      -H "Authorization: Bearer scope3_..." \
      -H "Content-Type: application/json" \
      -d '{
        "sandbox": true,
        "sourceId": "source_123",
        "sellerSandboxReady": true,
        "advertiserDomain": "brand.example",
        "campaignBrief": "Reach sports fans with display inventory.",
        "creativeReady": true,
        "budget": 500,
        "currency": "USD",
        "flightStart": "2026-08-01",
        "flightEnd": "2026-08-10",
        "packages": [{ "productId": "sports-display", "budget": 500 }]
      }'
    ```

    A complete, sandbox-ready plan returns `confirmation.status: "ready"` and a
    five-minute `planToken`. The token is scoped to the authenticated account,
    calling credential, and exact resolved source, advertiser ID/domain/currency,
    flight, budget, and products. Set `creativeReady: true` only when the required
    creative is ready or you explicitly intend a no-creative test. The token is
    single-use.

    Product candidates include display-only `pricing` summaries with the option
    name, rate, and currency when available. They deliberately omit pricing-option
    IDs: execution uses the buyer-facing discovery projection, including any
    currency conversion, rather than the source catalog shown by the plan. Omit
    `packages[].pricingOptionId` to let execution select a buyer-facing option. If
    you need to choose one explicitly, use the `pricingOptionId` returned by buyer
    discovery for the same product and currency.

    ```bash curl theme={null}
    curl -X POST https://api.interchange.io/api/v2/storefront/test-campaigns/execute \
      -H "Authorization: Bearer scope3_..." \
      -H "Content-Type: application/json" \
      -d '{ "planToken": "sf_test_plan_..." }'
    ```

    Execution accepts no replacement campaign fields: re-plan to change anything
    or after a token expires or is consumed. Inspect the latest result with
    `GET /api/v2/storefront/test-runs?take=1`, or the
    `list_agent_test_runs` named `storefront_api_call` operation. The matching
    named operations are `plan_storefront_test_campaign` and
    `execute_storefront_test_campaign`.

    For a deeper agent connectivity test (full AdCP compliance scenarios in sandbox mode), hit:

    <CodeGroup>
      ```bash curl theme={null}
      curl https://api.interchange.io/api/v2/storefront/readiness/compliance \
        -H "Authorization: Bearer scope3_..."
      ```
    </CodeGroup>

    This may take up to 60 seconds.

    #### 2. Resume buyer intake

    <CodeGroup>
      ```bash curl theme={null}
      curl -X PUT https://api.interchange.io/api/v2/storefront \
        -H "Authorization: Bearer scope3_..." \
        -H "Content-Type: application/json" \
        -d '{ "isPaused": false }'
      ```
    </CodeGroup>

    Clearing the compatibility-named `isPaused` intake hold does not claim the storefront is ready. The write succeeds, and the response still projects `blocked` with the current failing checks until every hard requirement passes. The intake hold controls buyer discovery, new buys, and buyer edits; it does not pause or resume campaigns already delivering in your ad server.

    ```json Response theme={null}
    {
      "isPaused": false,
      "canTransact": false,
      "effectiveStatus": "blocked"
    }
    ```

    When the last hard requirement becomes complete, the same storefront without an intake hold projects `live` automatically. If a future requirement is added or current evidence fails, it projects `blocked` automatically without rewriting `isPaused` or an `adcp_agent` row.

    #### 3. Marketplace review

    The marketplace review state is independent from the derived storefront lifecycle:

    | Marketplace state | Meaning                                                                                                                                   |
    | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
    | `PENDING_REVIEW`  | Default for new Storefronts. The owning organization can configure and use the Storefront, but it is not shown in public buyer discovery. |
    | `LISTED`          | Reviewed and visible in public buyer discovery and marketplace browsing, subject to the normal live/source/credential gates.              |
    | `HIDDEN`          | Intentionally removed from public buyer discovery, commonly for test, internal, or deprecated Storefronts.                                |

    Scope3 reviews newly live Storefronts before listing them. This keeps test
    Storefronts and unreviewed sellers out of the broader marketplace without
    blocking the operator's own setup work.

    #### Lifecycle states

    | Status     | Meaning                                                                                                  |
    | ---------- | -------------------------------------------------------------------------------------------------------- |
    | `PENDING`  | Created but not live. Buyer-side discovery does not surface it.                                          |
    | `ACTIVE`   | Live. Buyer agents can transact; public discovery also requires marketplace review and listing.          |
    | `DISABLED` | Temporarily off. Existing media buys retain their current state and route, but new ones can't be placed. |

    Allowed transitions: `PENDING ↔ ACTIVE ↔ DISABLED` (you can't go straight from `DISABLED` to `PENDING`).
  </Step>
</Steps>

***

## Troubleshooting

<Accordion title="`AAO compliance pending` — agent is in registry but tests are still running">
  This is logged but not a blocker. The source is created and will auto-activate once its authentication requirements are configured. Reachability is reported independently; no action is required unless source health flips to `not-passing`.
</Accordion>

<Accordion title="`AAO compliance not passing` — endpoint returned `not-passing`">
  This is an advisory warning, not a source-creation or activation blocker. Visit [agenticadvertising.org](https://agenticadvertising.org) and check the storyboard test results for your agent URL. Resolve the failing scenarios in your agent implementation so the registry verdict improves and marketplace review has a clean signal.
</Accordion>

<Accordion title="`Agent must be registered with AAO to connect`">
  The agent's `endpointUrl` doesn't appear in the AAO registry at all. Register it through the AAO operator dashboard before retrying.
</Accordion>

<Accordion title="`Cannot activate storefront: All agents must be active to go live`">
  Your agent record is `PENDING`. Most often this means the auth credential hasn't been verified yet. Re-submit the source with a fresh `auth` block, or for OAuth agents make sure the OAuth callback completed.
</Accordion>

<Accordion title="`Cannot activate storefront: All agents must be authenticated to go live`">
  A non-OAuth agent has no stored credential. `PUT /api/v2/storefront/inventory-sources/:sourceId` with an `auth` block to set one.
</Accordion>

<Accordion title="`agent_connectivity` failed in compliance check">
  Look at the `compliance` array on the check — each entry has per-track `failureReason`, `summary`, and `observations`. This check is informational for activation, but it is still useful debugging signal. The most common causes are auth misconfiguration, schema drift between your agent and the AdCP spec, and agent-side timeouts beyond 60s.
</Accordion>

<Accordion title="Storefront says `operatorDomainVerified: false` even though my domain matches">
  Matching the registered `customerDomain` is not enough by itself — the account domain must also be approved by active-member email ownership or Scope3 admin attestation. Same-value `operatorDomain` updates preserve the current verification state; have a Scope3 admin approve the account domain or change the storefront to the correct operator domain.
</Accordion>

<Accordion title="My account belongs to an organization — whose payout details apply?">
  An account inherits its organization's payout configuration. Only a parent
  organization admin can create or change a direct seller child's destination.
  In **Plan & Billing → Payouts**, select the child storefront; through REST,
  pass `?targetCustomerId=<account-id>`. A child-only admin cannot create an
  account-specific payout destination.
</Accordion>

***

## Adding a storefront account under an organization

Multi-brand publishers split into an organization with storefront accounts
under it. An organization admin provisions a new account via
`POST /api/v2/accounts/create-child` (or "Add account" in the UI). The account
inherits the parent organization's governing agreement and does not accept
separate account-level Terms of Service.

If the parent organization has no active governing agreement, user-info and
account-switch responses carry the flags the UI uses to gate the experience:

```json theme={null}
{
  "data": {
    "customer": { "id": 9101, "company": "..." },
    "showTosBox": true,
    "canAcceptTos": false,
    "organizationContractMissing": true
  }
}
```

`canAcceptTos: false` means the user is not a direct admin of the parent
organization, so the UI shows a blocking notice. The global account selector
remains available for account switching and pending organization invitations.
After an invited parent-organization ADMIN joins, they can accept the ToS and
unblock every inherited account. Team and Communications also remain available
before acceptance so organization admins can onboard members and configure a
support channel.

***

## Next steps

* [Prepare inventory source inputs](/v2/setup/publisher-onboarding-starter-kit) — when a source needs inputs supplied separately, identify where avails, products, CRM context, creative formats, properties, execution, and reporting come from; complete external-agent pass-through can skip it
* [Storefront API Reference](/v2/storefront-api-reference) — full endpoint reference for storefront, billing, and inventory source endpoints
* [Authentication](/v2/authentication) — API key and OAuth flows
* [Storefront object guide](/v2/object-guides/storefront) — how buyer agents see your storefront once it's live (discovery, credentials, sources)
