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.
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.
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
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.
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:
Status
Meaning
UNSUPPORTED
No active module implements the stage.
MISSING_SETUP
A module supports the stage, but required module config is missing.
RUNTIME_INPUTS_REQUIRED
Setup is complete, but the stage needs runtime data such as avails, creative refs, media-buy fields, or reporting uploads.
HITL_PENDING
Setup is complete and at least one open human work item is waiting for this stage.
READY
The stage has the setup and runtime inputs it needs for the current source state.
BLOCKED
A 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.
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:
State
Meaning
Creative transport
true
Seller has a creative library.
sync_creatives is required and packages reference synced manifests with creative_assignments.
false
Seller does not have a creative library.
Creative manifests are delivered inline on package payloads.
null or absent
Unknown, legacy, or synthetic capability metadata.
Interchange falls back to the advertised tool list and uses sync_creatives when available.
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.
Field
Type
Description
agentName
string
Deprecated agent-name field, kept for legacy compatibility only. Prefer the Storefront display name for buyer-facing naming. Maximum 80 characters.
agentPersonality
string
Voice and merchandising guidance for buyer-facing interactions and reports. Maximum 1000 characters.
publisherDomains
string[]
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.
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.
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:
Field
Type
Description
runCount
integer
Recent intelligence runs considered
bookedRunCount
integer
Runs with at least one attributed booked media buy
winRate
number
Share of recent runs that booked, from 0 to 1
askRunCount
integer
Runs with buyer asks, price objections, or packaging asks
askToBookConversionRate
number | null
Share of ask runs that booked, or null when no ask runs exist
priceObjectionConversionRate
number | null
Share of price-objection runs that booked, or null when none exist
packagingAskConversionRate
number | null
Share of packaging-ask runs that booked, or null when none exist
averageBookedBudget
number | null
Average booked budget per attributed booked media buy
averageProductsPerRun
number
Average products shown per run
averageProductsPerBookedRun
number | null
Average products shown on booked runs
averageProductsPerUnbookedRun
number | null
Average products shown on unbooked runs
deliveryRate
number | null
Delivery-report events divided by booked media buys, or null when none booked
repeatBuyerCount
integer
Buyer identities with at least two runs in the window
recommendedPosture
string | null
Directional 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:
Field
Type
Description
id
string
Stable signal key within the generated analytics payload
priority
"high" | "medium" | "low"
Suggested attention level
label
string
Short display label
detail
string
Human-readable reason for the signal
posture
string
Suggested negotiation posture
evidence
array
Small 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:
Field
Type
Description
comparableRunCount
integer
Runs where a recommended posture existed to compare the chosen posture against
followedCount
integer
Comparable runs where the chosen posture matched the recommendation
followedWinRate
number | null
Share of followed runs that booked, from 0 to 1, or null when no run had a recommendation to compare against
divergedWinRate
number | null
Share 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:
Field
Type
Description
attributedRunCount
integer
Number of recent runs with at least one attributed outcome event
eventCount
integer
Total attributed outcome events
submittedCount
integer
Media buys submitted for seller approval
forwardedCount
integer
Media buys forwarded to inventory sources
forwardFailedCount
integer
Media-buy forward attempts that failed
rejectedCount
integer
Media buys rejected by the seller
deliveryReportedCount
integer
Delivery report events attributed to recent runs
bookedMediaBuyCount
integer
Unique attributed media buys with a booked budget
bookedBudget
number
Sum of attributed booked budget in the analytics window, counted once per media buy
deliveredImpressions
integer
Sum of attributed delivered impressions
deliveredSpend
number
Sum of attributed delivered spend
deliveredCurrency
string | null
Delivery 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:
Field
Type
Description
eventCount
integer
Attributed outcome events for the run
latestType
string | null
Latest attributed event type, or null when no events are attributed
latestStatus
string | null
Latest media-buy status, or null when no status is available
mediaBuyId
string | null
Latest attributed media-buy ID, or null when no media buy is attributed
submittedCount
integer
Media buys submitted for seller approval
forwardedCount
integer
Media buys forwarded to inventory sources
forwardFailedCount
integer
Media-buy forward attempts that failed
rejectedCount
integer
Media buys rejected by the seller
deliveryReportedCount
integer
Delivery report events attributed to the run
bookedMediaBuyCount
integer
Unique attributed media buys with a booked budget
bookedBudget
number | null
Sum of attributed booked budgets for the run, counted once per media buy, or null when no budget is available
deliveredImpressions
integer
Delivered impressions attributed to the run
deliveredSpend
number
Delivered spend attributed to the run
deliveredCurrency
string | null
Delivery 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:
Field
Type
Description
operatorDomain
string | null
Buyer operator domain when known
brandDomain
string | null
Buyer brand domain when known
country
string | null
Buyer country when known
firstRunAt
string | null
Earliest run timestamp for this buyer in the analytics window
lastRunAt
string | null
Latest run timestamp for this buyer in the analytics window
runCount
integer
Recent intelligence runs for this buyer identity
bookedRunCount
integer
Runs for this buyer with at least one attributed booked media buy
winRate
number
Share of this buyer’s recent runs that booked, from 0 to 1
askRunCount
integer
Runs for this buyer with buyer asks, price objections, or packaging asks
askToBookConversionRate
number | null
Share of this buyer’s ask runs that booked, or null when no ask runs exist
attributedRunCount
integer
Runs with at least one attributed outcome event
eventCount
integer
Attributed outcome events for this buyer identity
totalShownProducts
integer
Products shown across the buyer’s runs
averageShownProducts
number
Average products shown per run
priceObjectionCount
integer
Runs with detected price objections
packagingAskCount
integer
Runs with detected packaging asks
requestAskCount
integer
Buyer request-level asks detected in the window
productAskCount
integer
Product-level asks detected in the window
proposalAskCount
integer
Proposal-level asks detected in the window
bookedMediaBuyCount
integer
Unique attributed media buys with a booked budget
bookedBudget
number
Sum of attributed booked budget for this buyer identity
averageBookedBudget
number | null
Average booked budget for this buyer’s attributed booked media buys
deliveredImpressions
integer
Delivered impressions attributed to this buyer identity
deliveredSpend
number
Delivered spend attributed to this buyer identity
deliveredCurrency
string | null
Delivery spend currency, or null when no delivery currency is known or currencies are mixed
recommendedPosture
string | null
Buyer-specific directional negotiation posture
The seasonality[] array buckets the same analytics window by UTC month:
Field
Type
Description
period
string
Month bucket in YYYY-MM form
runCount
integer
Intelligence runs in the period
bookedRunCount
integer
Runs in the period with at least one attributed booked media buy
winRate
number
Share of period runs that booked, from 0 to 1
askRunCount
integer
Period runs with buyer asks, price objections, or packaging asks
askToBookConversionRate
number | null
Share of period ask runs that booked, or null when no ask runs exist
priceObjectionCount
integer
Period runs with detected price objections
packagingAskCount
integer
Period runs with detected packaging asks
bookedBudget
number
Sum of attributed booked budget in the period
averageBookedBudget
number | null
Average booked budget across booked runs in the period
averageShownProducts
number
Average products shown per run in the period
recommendedPosture
string | null
Directional posture inferred for the period
The sellerRecommendations[] array turns those directional signals into
seller actions:
Field
Type
Description
id
string
Stable recommendation key within the generated analytics payload
Buyer identity the recommendation is about, or null for global seller guidance
evidence
array
Small 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.
Whether the buyer must register credentials to use this source
connected
boolean
Whether 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 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:
Field
Description
id, sourceId, name
Internal row id, storefront source id, and display name.
executionType, sourceStatus
Source type and current storefront source status.
capabilities
Support state for products, createMediaBuy, updateMediaBuy, signals, and wholesaleProducts. Synthetic or missing manifests report unknown capabilities rather than hard unsupported states.
auth
Whether source authentication is required, configured, and which auth type is expected.
productBuilder
Whether the source is used for product composition, pass-through routing, or is still unknown, plus wholesale product and signal counts when available.
compliance
Latest AAO compliance summary, including pass/fail state, checked timestamp, and track counts.
debug
Sanitized capability/debug metadata such as advertised tools, channels, publisher domains, and synthetic-manifest state.
lastActivity
Latest setup activity for the inventory source, when available.
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.
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.
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.
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:
Field
Type
Notes
storefrontIds
number[]
Up to 100 storefront IDs you’re interested in
notes
string
Free-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." }'
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.
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.
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.
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
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 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.
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:
Provider
provider value
Notes
Amazon
amazon
Sales adapter
AudioStack
audiostack
Creative adapter
Google Ads
google
Sales adapter
Meta
meta
Sales adapter
Pinterest
pinterest
Sales adapter
Reddit
reddit
Sales adapter
Snap
snap
Sales adapter
Spotify
spotify
Sales adapter
TikTok
tiktok
Sales adapter
Provider OAuth apps should allow the adapter callback path:
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".