Skip to main content

Overview

A Storefront is a publisher’s buyer-facing home on Interchange: the business presence, name, description, and discovery surface buyers use to understand who they are buying from. The customer’s Merchandising Agent runs that Storefront. It implements the AdCP media-buy workflow, responds to buyer briefs, and draws from one or more inventory sources. Inventory sources can be external sales agents you already run, your own ad servers with Interchange-managed sales-agent plumbing behind them, or other Storefronts that have authorized passthrough/federation. Buying through a storefront is gated on credentials per source: every inventory source declares whether it requires authentication, and the buyer registers credentials (API key, JWT, or OAuth) once per source. Once a source reports connected: true, discovery and media buys flow through it normally.
Storefront
  └── Inventory Source(s)        (MCP / A2A agent)
        └── Buyer credentials    (per source, per buyer)
              └── Customer accounts
This page covers the buyer side: browsing storefronts, expressing interest in upcoming supply, and wiring up credentials. If you operate a storefront, see Storefront onboarding.
All examples below use the buyer base URL:
https://api.interchange.io/api/v2/buyer

Why storefronts matter

  • One-stop access to publisher inventory — a storefront aggregates multiple inventory sources behind a single ID, so buyers don’t track agent endpoints individually
  • Per-source credential management — credentials are scoped to the inventory source, so rotating or revoking one doesn’t affect access to the others
  • AAO compliance gating — marketplace storefronts are gated on the Agentic Advertising Organization registry; only compliant sources surface to buyers
  • Multiple auth options — sources support API key, JWT, and OAuth flows, so the platform fits whatever the publisher’s agent already uses
  • Discoverable upcoming supply/storefronts/discover and /storefronts/interest let buyers see and request access to publishers they aren’t yet connected to

Storefronts in buyer discovery

Every transacting, marketplace-listed Storefront is exposed through the customer’s Merchandising Agent for buyer-side product discovery. The generated sales agent ID is storefront-{platform_id}, where platform_id is the Storefront’s public platformId slug; for example, a Storefront with platformId: "premium-ctv" appears as salesAgentId: "storefront-premium-ctv" in discovery results. Existing Storefronts are registered automatically once listed, so buyers can use the standard discovery flow without learning a separate endpoint. The Merchandising Agent is the buyer-facing ADCP actor for the Storefront, not a replacement for inventory sources, source IDs, or underlying source agent IDs. Composition Storefronts return products assembled from active ingredient sources and active product rules. Passthrough Storefronts proxy get_products to an active upstream inventory source and apply Storefront identity, metadata, and buyer-instruction overlays to the response. Buyer instructions are resolved from operator domain, brand domain, and optional country.
New Storefronts start in a pending-review marketplace state. They can be configured and used by the owning organization, and they can open for known transactions, but public buyer discovery only includes Storefronts that Scope3 has reviewed and listed. Admins can also hide a listed Storefront, which removes it from public buyer discovery without deleting the Storefront or its inventory sources.

Key fields

The storefront resource has two response shapes:
  • Summary — returned by GET /storefronts (list). Row-level fields plus scalar source counts.
  • Full — returned by GET /storefronts/:id (get). The complete resource with the embedded sources[] array and per-source customer accounts.

Modular inventory source readiness

Operator-owned storefronts can include MODULAR_SOURCE inventory sources. A modular source is composed from smaller source-side modules: inventory-feed ingestion, a booking ledger, execution handoff, creative mapping, status sync, reporting import, and source-side human work queues. The modular source runtime projection includes lifecycleSummary, which is the operator-facing rollup for each stage:
StatusMeaning
UNSUPPORTEDNo active module implements the stage.
MISSING_SETUPA module supports the stage, but required module config is missing.
RUNTIME_INPUTS_REQUIREDSetup is complete, but the stage needs runtime data such as avails, creative refs, media-buy fields, or reporting uploads.
HITL_PENDINGSetup is complete and at least one open human work item is waiting for this stage.
READYThe stage has the setup and runtime inputs it needs for the current source state.
BLOCKEDA module has a source-health issue or error that needs operator attention.
activeAvailCount is the number of active normalized avails available to the source projection. openWorkItemCountsByKind groups source-side queue items by kind, such as CONFIRM_AVAILS, TRAFFIC_TO_CADENT, MAP_CREATIVE_REF, or UPLOAD_FINAL_REPORT. Use the detailed modular inventory source lifecycle guide for the operator task sequence: preview and commit avails, inspect products, reserve capacity, prepare execution handoff, release capacity, and work HITL queue items.

Creative library capability

Storefront and source capability caches store AdCP creative.has_creative_library as creativeHasCreativeLibrary. Interchange uses this tri-state field to choose creative transport for media buys:
StateMeaningCreative transport
trueSeller has a creative library.sync_creatives is required and packages reference synced manifests with creative_assignments.
falseSeller does not have a creative library.Creative manifests are delivered inline on package payloads.
null or absentUnknown, legacy, or synthetic capability metadata.Interchange falls back to the advertised tool list and uses sync_creatives when available.

Operator businessProfile

Operator setup surfaces may include a businessProfile object captured during Murph-led storefront setup. This profile helps the Merchandising Agent describe the operator’s business, follow the right merchandising guidance, and understand which publisher domains it should be authorized to sell through the Storefront.
FieldTypeDescription
agentNamestringDeprecated agent-name field, kept for legacy compatibility only. Prefer the Storefront display name for buyer-facing naming. Maximum 80 characters.
agentPersonalitystringVoice and merchandising guidance for buyer-facing interactions and reports. Maximum 1000 characters.
publisherDomainsstring[]Publisher domains or properties the Merchandising Agent should be authorized to sell through the Storefront. Up to 128 valid domains; each domain is normalized to lowercase, deduplicated, and limited to 253 characters.
Example
{
  "businessProfile": {
    "agentPersonality": "Confident, concise, and commercially sharp.",
    "publisherDomains": ["touchline.example", "football.touchline.example"]
  }
}
Omitting publisherDomains means the authorized domains are unknown or have not been captured yet. It does not mean the Merchandising Agent is authorized to sell every domain through the Storefront.

Murph sellerAnalytics

When a storefront operator asks Murph for seller analytics, Murph can attach a sellerAnalytics payload to the chat response for visual MCP App rendering. The payload includes discovery-run mix, buyer asks, top surfaced products, and commercial outcomes attributed to those runs. The historicalPerformance object summarizes the same window into the negotiation signals Murph and Chef can use:
FieldTypeDescription
runCountintegerRecent intelligence runs considered
bookedRunCountintegerRuns with at least one attributed booked media buy
winRatenumberShare of recent runs that booked, from 0 to 1
askRunCountintegerRuns with buyer asks, price objections, or packaging asks
askToBookConversionRatenumber | nullShare of ask runs that booked, or null when no ask runs exist
priceObjectionConversionRatenumber | nullShare of price-objection runs that booked, or null when none exist
packagingAskConversionRatenumber | nullShare of packaging-ask runs that booked, or null when none exist
averageBookedBudgetnumber | nullAverage booked budget per attributed booked media buy
averageProductsPerRunnumberAverage products shown per run
averageProductsPerBookedRunnumber | nullAverage products shown on booked runs
averageProductsPerUnbookedRunnumber | nullAverage products shown on unbooked runs
deliveryRatenumber | nullDelivery-report events divided by booked media buys, or null when none booked
repeatBuyerCountintegerBuyer identities with at least two runs in the window
recommendedPosturestring | nullDirectional negotiation posture: hold_value, value_preserving_compromise, tradeoff_ladder, direct_fit, price_first, or wholesale_mirror
The strategySignals[] array turns historicalPerformance into deterministic strategy guidance for sellers:
FieldTypeDescription
idstringStable signal key within the generated analytics payload
priority"high" | "medium" | "low"Suggested attention level
labelstringShort display label
detailstringHuman-readable reason for the signal
posturestringSuggested negotiation posture
evidencearraySmall list of { label, value } metrics backing the signal
The postureConversion.adherence object reports how often the negotiation posture the agent actually used matched the history-derived recommendation, and how win rate differs when the seller follows that recommendation versus diverges from it:
FieldTypeDescription
comparableRunCountintegerRuns where a recommended posture existed to compare the chosen posture against
followedCountintegerComparable runs where the chosen posture matched the recommendation
followedWinRatenumber | nullShare of followed runs that booked, from 0 to 1, or null when no run had a recommendation to compare against
divergedWinRatenumber | nullShare of diverged runs that booked, from 0 to 1, or null when no run had a recommendation to compare against
When Murph composes products, the same recent outcome history can tune Chef’s built-in negotiation defaults, including posture selection, target product count, pricing guidance, and packaging guidance. Human operating instructions and buyer-specific instruction notes remain authoritative over these historical signals. The top-level outcomes object summarizes attributed outcomes across the window:
FieldTypeDescription
attributedRunCountintegerNumber of recent runs with at least one attributed outcome event
eventCountintegerTotal attributed outcome events
submittedCountintegerMedia buys submitted for seller approval
forwardedCountintegerMedia buys forwarded to inventory sources
forwardFailedCountintegerMedia-buy forward attempts that failed
rejectedCountintegerMedia buys rejected by the seller
deliveryReportedCountintegerDelivery report events attributed to recent runs
bookedMediaBuyCountintegerUnique attributed media buys with a booked budget
bookedBudgetnumberSum of attributed booked budget in the analytics window, counted once per media buy
deliveredImpressionsintegerSum of attributed delivered impressions
deliveredSpendnumberSum of attributed delivered spend
deliveredCurrencystring | nullDelivery spend currency, or null when no delivery currency is known or currencies are mixed
Each item in runs[] includes an outcome object for that run:
FieldTypeDescription
eventCountintegerAttributed outcome events for the run
latestTypestring | nullLatest attributed event type, or null when no events are attributed
latestStatusstring | nullLatest media-buy status, or null when no status is available
mediaBuyIdstring | nullLatest attributed media-buy ID, or null when no media buy is attributed
submittedCountintegerMedia buys submitted for seller approval
forwardedCountintegerMedia buys forwarded to inventory sources
forwardFailedCountintegerMedia-buy forward attempts that failed
rejectedCountintegerMedia buys rejected by the seller
deliveryReportedCountintegerDelivery report events attributed to the run
bookedMediaBuyCountintegerUnique attributed media buys with a booked budget
bookedBudgetnumber | nullSum of attributed booked budgets for the run, counted once per media buy, or null when no budget is available
deliveredImpressionsintegerDelivered impressions attributed to the run
deliveredSpendnumberDelivered spend attributed to the run
deliveredCurrencystring | nullDelivery spend currency, or null when no delivery currency is known or currencies are mixed
latestType is one of media_buy_submitted_for_approval, media_buy_forwarded, media_buy_forward_failed, media_buy_rejected, or delivery_reported. The buyers[] array rolls the same window up by buyer identity:
FieldTypeDescription
operatorDomainstring | nullBuyer operator domain when known
brandDomainstring | nullBuyer brand domain when known
countrystring | nullBuyer country when known
firstRunAtstring | nullEarliest run timestamp for this buyer in the analytics window
lastRunAtstring | nullLatest run timestamp for this buyer in the analytics window
runCountintegerRecent intelligence runs for this buyer identity
bookedRunCountintegerRuns for this buyer with at least one attributed booked media buy
winRatenumberShare of this buyer’s recent runs that booked, from 0 to 1
askRunCountintegerRuns for this buyer with buyer asks, price objections, or packaging asks
askToBookConversionRatenumber | nullShare of this buyer’s ask runs that booked, or null when no ask runs exist
attributedRunCountintegerRuns with at least one attributed outcome event
eventCountintegerAttributed outcome events for this buyer identity
totalShownProductsintegerProducts shown across the buyer’s runs
averageShownProductsnumberAverage products shown per run
priceObjectionCountintegerRuns with detected price objections
packagingAskCountintegerRuns with detected packaging asks
requestAskCountintegerBuyer request-level asks detected in the window
productAskCountintegerProduct-level asks detected in the window
proposalAskCountintegerProposal-level asks detected in the window
bookedMediaBuyCountintegerUnique attributed media buys with a booked budget
bookedBudgetnumberSum of attributed booked budget for this buyer identity
averageBookedBudgetnumber | nullAverage booked budget for this buyer’s attributed booked media buys
deliveredImpressionsintegerDelivered impressions attributed to this buyer identity
deliveredSpendnumberDelivered spend attributed to this buyer identity
deliveredCurrencystring | nullDelivery spend currency, or null when no delivery currency is known or currencies are mixed
recommendedPosturestring | nullBuyer-specific directional negotiation posture
The seasonality[] array buckets the same analytics window by UTC month:
FieldTypeDescription
periodstringMonth bucket in YYYY-MM form
runCountintegerIntelligence runs in the period
bookedRunCountintegerRuns in the period with at least one attributed booked media buy
winRatenumberShare of period runs that booked, from 0 to 1
askRunCountintegerPeriod runs with buyer asks, price objections, or packaging asks
askToBookConversionRatenumber | nullShare of period ask runs that booked, or null when no ask runs exist
priceObjectionCountintegerPeriod runs with detected price objections
packagingAskCountintegerPeriod runs with detected packaging asks
bookedBudgetnumberSum of attributed booked budget in the period
averageBookedBudgetnumber | nullAverage booked budget across booked runs in the period
averageShownProductsnumberAverage products shown per run in the period
recommendedPosturestring | nullDirectional posture inferred for the period
The sellerRecommendations[] array turns those directional signals into seller actions:
FieldTypeDescription
idstringStable recommendation key within the generated analytics payload
priority"high" | "medium" | "low"Suggested attention level
kind"protect_expand" | "prioritize_follow_up" | "packaging_friction" | "price_resistance" | "tighten_selection"Recommendation category
titlestringShort display title
detailstringHuman-readable reason for the recommendation
actionstringSuggested next seller action
buyerobject | nullBuyer identity the recommendation is about, or null for global seller guidance
evidencearraySmall list of { label, value } metrics backing the recommendation
Seller analytics outcome totals use last-touch product-overlap attribution and are directional. Seller recommendations are deterministic coaching signals derived from the same window, not automated pricing or packaging changes. Use buyer reporting endpoints for audited campaign delivery reporting.

BuyerStorefrontSummary (list rows)

FieldTypeDescription
idintegerStorefront ID
platformIdstringPublic-facing slug
namestringStorefront display name
publisherDomainstring | nullPublisher domain
sourceCountintegerTotal number of inventory sources in this storefront
connectedSourceCountintegerNumber of sources the buyer already has working credentials for
Use GET /storefronts/:id when you need the full source list (credential state, customer accounts).

BuyerStorefront (single storefront)

FieldTypeDescription
idintegerStorefront ID
platformIdstringPublic-facing slug
namestringStorefront display name
publisherDomainstring | nullPublisher domain
sourcesBuyerStorefrontSource[]Inventory sources available in this storefront

BuyerStorefrontSource

FieldTypeDescription
sourceIdstringInventory source identifier within the storefront
namestringInventory source display name
requiresCredentialsbooleanWhether the buyer must register credentials to use this source
connectedbooleanWhether the buyer has active credentials for this source
customerAccounts[{ accountIdentifier, status }]Buyer’s registered accounts for this source
Detecting whether a source uses OAuth. BuyerStorefrontSource does not expose an explicit authType field. To find out which auth modes a source accepts:
  • requiresCredentials: false — no auth needed; you can call discovery and media-buy endpoints directly.
  • requiresCredentials: true — POST to the credentials endpoint without a body; the validation error response lists the supported auth types (api_key, jwt, oauth). Alternatively, query the underlying agent’s AdCP capabilities, which advertise the supported authentication types.
If OAuth is in the list, follow the OAuth flow below instead of POSTing to the credentials endpoint.

Storefront readiness diagnostics

Storefront operators can call GET /api/v2/storefront/readiness to inspect setup progress. In addition to the top-level readiness checks, the response includes sourceDiagnostics[], a per-inventory-source diagnostic summary used by Murph and the storefront setup rail. Each source diagnostic includes:
FieldDescription
id, sourceId, nameInternal row id, storefront source id, and display name.
executionType, sourceStatusSource type and current storefront source status.
capabilitiesSupport state for products, createMediaBuy, updateMediaBuy, signals, and wholesaleProducts. Synthetic or missing manifests report unknown capabilities rather than hard unsupported states.
authWhether source authentication is required, configured, and which auth type is expected.
productBuilderWhether the source is used for product composition, pass-through routing, or is still unknown, plus wholesale product and signal counts when available.
complianceLatest AAO compliance summary, including pass/fail state, checked timestamp, and track counts.
debugSanitized capability/debug metadata such as advertised tools, channels, publisher domains, and synthetic-manifest state.
lastActivityLatest setup activity for the inventory source, when available.

Common operations

Listing storefronts

GET /storefronts returns the storefronts you can transact with, paginated.
curl "https://api.interchange.io/api/v2/buyer/storefronts?limit=20" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
Query parameters:
ParamNotes
nameCase-insensitive partial match on storefront name
limit1..50 (default 20)
offsetPagination offset (default 0)
Response (summary rows):
{
  "items": [
    {
      "id": 42,
      "platformId": "premium-ctv",
      "name": "Premium CTV Storefront",
      "publisherDomain": "premium-ctv.example.com",
      "sourceCount": 3,
      "connectedSourceCount": 1
    }
  ],
  "total": 1,
  "hasMore": false,
  "nextOffset": null
}
For the full source list — credential state, customer accounts — call GET /storefronts/:id.
When hasMore is true, pass nextOffset back as the offset parameter to walk pages.

Getting a storefront

GET /storefronts/:id returns the full BuyerStorefront resource — including the embedded sources[] array with per-source requiresCredentials, connected, and customerAccounts. Use this when you need fresh source state (e.g. after registering credentials or completing the OAuth flow) or when a row-level summary isn’t enough.
curl "https://api.interchange.io/api/v2/buyer/storefronts/42" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
Response
{
  "id": 42,
  "platformId": "premium-ctv",
  "name": "Premium CTV Storefront",
  "publisherDomain": "premium-ctv.example.com",
  "sources": [
    {
      "sourceId": "src_main",
      "name": "Premium CTV — Direct",
      "requiresCredentials": true,
      "connected": false,
      "customerAccounts": []
    }
  ]
}

Discovery (lightweight)

GET /storefronts/discover returns every marketplace-listed storefront visible to a buyer - the live ones (TRANSACTING) and any listed storefront still working through the pre-launch lifecycle (UNCLAIMED, VERIFICATION, CONFIGURING, TESTING) - with just id, name, publisherDomain, and status. This is the right endpoint for buyers who don’t yet have programmatic access but want to see what supply is coming.
curl "https://api.interchange.io/api/v2/buyer/storefronts/discover" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
{
  "items": [
    {
      "id": 42,
      "name": "Premium CTV Storefront",
      "publisherDomain": "premium-ctv.example.com",
      "status": "TRANSACTING"
    },
    {
      "id": 77,
      "name": "Travel Vertical Network",
      "publisherDomain": null,
      "status": "CONFIGURING"
    }
  ]
}
Discovery is intentionally minimal — no source list, no credential state. Use it to populate a “what’s available” picker, then submit interest with the IDs you care about.

Submitting interest

For private or upcoming storefronts you don’t yet have access to, submit an interest form. Scope3’s onboarding team picks it up via Slack. POST /storefronts/interest accepts:
FieldTypeNotes
storefrontIdsnumber[]Up to 100 storefront IDs you’re interested in
notesstringFree-form notes, up to 4000 chars
You must provide at least one of storefrontIds or notes.
curl -X POST "https://api.interchange.io/api/v2/buyer/storefronts/interest" \
  -H "Authorization: Bearer $SCOPE3_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "storefrontIds": [42, 77],
    "notes": "Targeting Q3 CTV upfronts; need access to travel vertical."
  }'
{
  "submitted": true,
  "submittedAt": "2026-04-26T15:00:00.000Z"
}
To check whether you’ve already submitted, call GET /storefronts/interest:
curl "https://api.interchange.io/api/v2/buyer/storefronts/interest" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
{
  "submitted": true,
  "submittedAt": "2026-04-26T15:00:00.000Z"
}

Listing your credentials

GET /storefronts/credentials returns every credential you’ve registered, scoped to the storefront sources each credential gives access to. Use it to build a single “connected sources” view in your dashboard without paging through each storefront.
curl "https://api.interchange.io/api/v2/buyer/storefronts/credentials" \
  -H "Authorization: Bearer $SCOPE3_API_KEY"
Response
[
  {
    "id": "722",
    "accountIdentifier": "your-account-id",
    "accountType": "CLIENT",
    "status": "ACTIVE",
    "registeredBy": "user@example.com",
    "createdAt": "2026-02-23T19:55:11.602Z",
    "updatedAt": "2026-02-23T19:56:56.272Z",
    "sources": [
      {
        "storefrontId": 42,
        "storefrontName": "Premium CTV Storefront",
        "sourceId": "src_main",
        "sourceName": "Premium CTV — Direct"
      }
    ]
  }
]
Each credential lists every (storefrontId, sourceId) pair it covers in sources[]. A single credential can cover the same source across multiple storefronts when the underlying agent is shared.

Registering credentials per source

For sources that report requiresCredentials: true, you must register credentials before you can run discovery or create media buys. POST /storefronts/:storefrontId/sources/:sourceId/credentials
Inspect the source’s requiresCredentials field on the parent storefront before calling this endpoint — sources that report requiresCredentials: false don’t need this step.
curl -X POST \
  "https://api.interchange.io/api/v2/buyer/storefronts/42/sources/src_main/credentials" \
  -H "Authorization: Bearer $SCOPE3_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountIdentifier": "your-account-id",
    "auth": {
      "type": "api_key",
      "token": "<SOURCE_API_KEY>"
    }
  }'
Body fields:
FieldTypeNotes
accountIdentifierstringUnique account ID at the source (1..255 chars)
authobjectAPI key or JWT credentials. Required for non-OAuth sources.
marketplaceAccountbooleanAdmin-only. When true, creates a marketplace account instead of a client account.
Auth tokens grant AdCP access on your behalf. Treat auth.token (and any private keys) as a production secret:
  • Store it in a managed secret vault. Scope3 itself stores it in Google Secret Manager and references it via auth_secret_ref.
  • Never log the raw token, never echo it to stdout, and never commit it to source control.
  • Rotate on any suspected exposure and on a documented schedule.
  • Use placeholders like <SOURCE_API_KEY> in documentation and sample requests — never a real key.

Registering an account for a source

When you want a source to bill or report against a specific advertiser seat in your account, register an account with advertiserId: POST /storefronts/:storefrontId/sources/:sourceId/accounts
curl -X POST \
  "https://api.interchange.io/api/v2/buyer/storefronts/42/sources/src_main/accounts" \
  -H "Authorization: Bearer $SCOPE3_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "advertiserId": "12345",
    "accountIdentifier": "acme-brand-account",
    "auth": {
      "type": "api_key",
      "token": "<SOURCE_API_KEY>"
    }
  }'
Body fields:
FieldTypeNotes
advertiserIdstringNumeric seat ID to associate with this account
accountIdentifierstringUnique account ID at the source (1..255 chars)
authobjectCredentials. Required for API_KEY/JWT sources, omitted for OAuth

OAuth flow for OAuth-secured sources

Some inventory sources use OAuth. In that case, the credentials endpoint isn’t enough — the buyer must complete an interactive consent flow, and the source agent exchanges an authorization code for tokens via the storefront OAuth-authorize endpoint.
The OAuth endpoints sit on the /api/v2/storefront/... mount because they power the storefront’s auth integration on behalf of buyers and sellers alike. Buyers reach them by agentId — the underlying AdCP agent that backs the inventory source. (You can discover the agent ID from the storefront’s source list, or via the Discovery guide.)
Step 1 — Request an authorization URL.
curl -X POST \
  "https://api.interchange.io/api/v2/storefront/agents/agt_premium_ctv/accounts/oauth/authorize" \
  -H "Authorization: Bearer $SCOPE3_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountIdentifier": "acme-brand-account"
  }'
accountIdentifier is optional — when omitted, the platform uses oauth_<customerId> as the placeholder identifier and you can rename the account later.
Response
{
  "authorizeUrl": "https://idp.example-publisher.com/oauth/authorize?client_id=...&redirect_uri=https%3A%2F%2Fapi.interchange.io%2Fapi%2Fv2%2Fstorefront%2Foauth%2Fcallback&state=pending_abc123&scope=adcp%3Aread+adcp%3Awrite",
  "state": "pending_abc123"
}
Step 2 — Send the operator to authorizeUrl. Open it in a popup or new tab. The publisher’s IdP authenticates the user, asks them to consent, and redirects back to the platform-hosted callback (/api/v2/storefront/oauth/callback). Step 3 — The platform finishes the exchange. The callback page exchanges the authorization code for tokens, stores them under your customer + the source’s underlying agent, and notifies the opener window via postMessage({ type: 'oauth-complete' }) before auto-closing. Refresh GET /api/v2/buyer/storefronts/:storefrontId after that — the source should now report connected: true. If you need to drive the callback yourself (for example, in a non-browser agent), POST /api/v2/storefront/agents/:agentId/oauth/callback accepts the code and state directly. The seller side of OAuth (callback URLs, state handling, secret rotation) is documented in Storefront onboarding.

Adapter-routed storefront providers

Some storefronts route directly to a wired provider adapter instead of an inventory-source sales agent. These adapter-routed storefronts still appear to buyers through the normal storefront discovery and media-buy surfaces, but the operator connects the upstream provider account through delegated OAuth before server-side workflows can run. Supported wired adapter providers are:
Providerprovider valueNotes
AmazonamazonSales adapter
AudioStackaudiostackCreative adapter
Google AdsgoogleSales adapter
MetametaSales adapter
PinterestpinterestSales adapter
RedditredditSales adapter
SnapsnapSales adapter
SpotifyspotifySales adapter
TikToktiktokSales adapter
Provider OAuth apps should allow the adapter callback path:
https://api.interchange.io/oauth/adapters/{provider}/callback
Use the same path on staging, for example https://api.staging.interchange.io/oauth/adapters/snap/callback.

Endpoint reference

MethodPathPurpose
GET/storefrontsList storefronts (paginated)
GET/storefronts/:storefrontIdGet a single storefront
GET/storefronts/discoverLightweight list of all storefronts (active + upcoming)
GET/storefronts/interestCheck your interest submission status
POST/storefronts/interestSubmit interest in one or more storefronts
GET/storefronts/credentialsList your credentials across all storefronts
POST/storefronts/:storefrontId/sources/:sourceId/credentialsRegister credentials for a source
POST/storefronts/:storefrontId/sources/:sourceId/accountsRegister an account (with advertiser linkage)
When a source isn’t returning products as expected, check connected on that source via GET /storefronts/:id — if it’s false, your credentials aren’t yet active. If it’s true, fall through to the Discovery flow with proposal.action: "discover".

Buyer onboarding

End-to-end setup for a new buyer customer.

Discovery

Once your sources are connected, run discovery to find products.

Advertiser

Advertisers are the seats you link to source accounts.

Storefront onboarding

Operating a storefront? Start here for the seller-side setup.