Skip to main content
Apostra is built on the Ad Context Protocol (AdCP). This page covers the buy side — how Apostra, acting as a buyer platform, consumes AdCP across many sellers.
This is the buy-side view. For the seller (supply) side — connecting your own AdCP sales agent as a Seller Account source — see Connect your sales agent. When this page and the AdCP spec disagree on what the protocol means, the AdCP spec wins.
The external-buyer campaign spine described below is alpha and available only on Sellers with the Listing + Distribution package. It supports governed create, update, cancellation, and live delivery through platform-composed hosted Sellers and direct-adapter Sellers. Adding packages during an endpoint update, change webhooks, and scheduled reporting exports are not part of this slice.

Two AdCP directions, one direct campaign mode

AdCP addresses one Seller at a time. Whether the Seller is reached through an external buyer endpoint or a connected account, the campaign carries mode: "directed" — a compatibility wire value; read the campaign’s management state for what the platform does with it: A task addressed directly to one Seller carries mode: "directed" because Apostra does not perform cross-Seller discovery or allocation. A discovery campaign can still finish with one seller when Apostra performed that selection; the result count does not change its mode. See Campaigns on connected provider accounts for the shared model. You may also hear this called a mediated AdCP campaign. Mediation is the platform invariant, so these docs use inbound when distinguishing the protocol direction. Neither “inbound” nor “mediated” is a campaign mode.

The cross-seller pattern

The defining difference between AdCP and Apostra on the buy side is the same everywhere: AdCP defines a task against one agent; Apostra runs it across many. That fan-out is our core scaling layer. An external AdCP client can also call one Apostra-hosted Seller directly. That is deliberately a single-seller protocol interaction, while Apostra still provides the buyer identity, contract, governance, audit, campaign, and ledger around the seller call.

Compliance at the edges, scale above them

Apostra does not ask a seller to understand a multi-seller request. The buyer-facing ext.interchange object selects Sellers, controls progressive polling, and optionally requests managed screening. Apostra then projects the canonical part of the request onto separate one-to-one get_products calls. Each seller returns its normal AdCP products/proposals; Apostra validates that response independently before aggregation. The aggregate page stays bounded rather than becoming one giant catalog:
  • pagination.max_results caps proposal and proposal-less-product candidates;
  • products referenced by a paged proposal travel with that proposal so its allocation graph is not split;
  • revisioned snapshots expose fast sellers while slower sellers remain pending;
  • cursors become stable only after all reachable sellers settle; and
  • storefront-qualified sf1: / sfp1: identities prevent collisions and are preserved until the bilateral purchase edge.
Optional screening is also above the protocol. It evaluates each valid proposal against buyer instructions and returns accept, reject, or refine. A refine decision is sent back to the originating seller through canonical AdCP; only the seller can change pricing, allocations, availability, or terms.

Conformance (buy side)

Status: 1:1 conforms as written · +ext with an Apostra scaling layer · none not implemented yet.

External buyer to Apostra to seller (alpha)

An external AdCP client follows the Seller’s Discovery Card action. On a verified Seller CNAME the endpoint is:
The platform-managed fallback is:
/adcp/mcp is deliberately stable and unversioned. MCP/AdCP negotiates a compatible protocol version directly, avoiding redirects for authenticated MCP POST requests. Every hosted Seller has one deterministic endpoint. The endpoint is the Seller’s external demand surface for AdCP buyers: it exposes that Seller’s products and directed media-buy lifecycle, rather than performing Apostra’s cross-seller discovery or allocation. The endpoint can exist before the Seller has Listing + Distribution; campaign-spine write tasks remain fail-closed until that Seller package is active. Use the normal authenticated AdCP 3.0/3.1 task lifecycle:
  1. Call get_adcp_capabilities and get_products on that Seller.
  2. Send create_media_buy with an idempotency_key, brand identity, flight, currency, and one or more explicitly budgeted packages.
  3. Use the returned media_buy_id with get_media_buys and get_media_buy_delivery.

Country and channel routing

For an external sales agent, Apostra reads portfolio coverage from get_adcp_capabilities.media_buy.portfolio:
  • primary_channels and primary_countries are the declared coverage for that source. Apostra keeps the two arrays correlated, and accepted countries equal primary countries.
  • Coverage from separate sources is never combined to manufacture a match.
There is no separate local accepted-country allowlist for a pure pass-through storefront. After the staged buyer-routing flag is enabled, Apostra includes a source only when both primary arrays are complete and match the requested coverage. A missing or malformed primary array is unknown and is a go-live readiness gap. Before that flag is enabled, legacy product fan-out remains unchanged. Missing primary coverage means unknown, not global coverage. Apostra does not infer an upstream agent’s countries from the storefront listing or business profile.

Linked account scope

When a request carries a linked storefront account_id, Apostra resolves the authenticated buyer’s grant before contacting any inventory source. Each source receives only the account identifier the seller assigned to that source; the linked Apostra account ID is never forwarded as if it were a source account. get_products returns structured AdCP errors when the account cannot be used: Missing, ambiguous, or stale source mappings are excluded from discovery without calling those sources. If every source required by create_media_buy lacks a healthy mapping, the tool returns ACCESS_DENIED with details.reason: "source_account_not_bound" and the coverage state in details.per_source. The seller must repair the mapping; the buyer then runs get_products again and submits a new media buy. Calls without a linked account and legacy natural-key accounts are unchanged. A transient account-resolution failure instead returns SERVICE_UNAVAILABLE with details.reason: "account_context_unavailable"; retry the same request after a short delay rather than changing the mapping. The same linked account now scopes media-buy reads. Call get_media_buys with the opaque account_id and no media_buy_ids to enumerate the mapped account across every inventory source that has a healthy native-account mapping. Apostra drains each source’s pages within a bounded discovery window, qualifies colliding native IDs into stable storefront media_buy_id values, and persists the route so get_media_buy_delivery can reach the same source later. Pass the same account and buyer credential on the delivery call; Apostra substitutes the source-native account again. To include terminal history, supply the desired status_filter values—an omitted filter keeps each source’s normal AdCP default. A targeted get_media_buys call with media_buy_ids reads already discovered storefront IDs and does not re-enumerate sources. Follow pagination.cursor to read the complete combined result. If one mapped source is unavailable, the completed response contains the buys from healthy sources plus a generic SOURCE_UNAVAILABLE entry; it never exposes the source name, native account ID, or provider error. If every mapped source is unavailable and there is no persisted result to return, the task fails with SERVICE_UNAVAILABLE and details.reason: "account_media_buy_sources_unavailable". Sources marked not_required are not enumerated for account history: an unscoped campaign list could expose other buyers. Account-enumerated buys are tracked, read-only records; create a managed buy through the normal product and media-buy flow instead of updating a discovered buy. Webhook activity is not included in this surface because the upstream call uses the storefront’s source credential, not the linked buyer principal. On a platform-composed storefront endpoint, the same linked account can list audience, signal, and event-source references with Apostra MCP extension get_account_resources. Direct adapter storefronts do not expose this tool. This is a live, read-only call: Apostra verifies the active grant for the authenticated buyer principal before contacting a source, forwards each eligible source only its own native account reference, and returns opaque storefront-qualified resource IDs. It never returns private source IDs, source-native account IDs, audience membership, setup secrets, or event payloads. Every source/resource-kind pair reports capability, coverage, authoritative, observed_at, item/exclusion counts, truncation, and a generic error code when a provider read fails. Missing, ambiguous, stale, and not_required source mappings are never called. Demo or placeholder resources are explicitly labeled non-authoritative. Results default to 50 resources and accept pagination.max_results up to 100; follow the opaque cursor while pagination.has_more is true. A request is also bounded by a 30-second live-read window and a 1,000-resource safety ceiling, so partial or truncated must be checked before treating the result as a complete account inventory. This is different from the tracked-campaign .../presences endpoint. That endpoint reads a background mirror for subscribed connection accounts; get_account_resources uses the linked account’s principal grant directly. The same buyer credential must resolve to an Apostra buyer account. A transacting credential should be scoped to an advertiser. During the alpha, an unscoped credential may resolve by brand.domain only when that domain identifies exactly one active advertiser in the buyer account; missing or ambiguous identity fails closed.

What Apostra adds before seller dispatch

For an enrolled relationship, media-buy writes do more than relay the protocol request:
  • checks a buyer/storefront currency policy plus per-buy and aggregate exposure caps;
  • journals create, update, and cancellation idempotently so an uncertain seller outcome is not silently dispatched twice;
  • creates the compatibility mode: directed campaign projection, media buy, and package records in the buyer account;
  • pins the buyer’s zero-fee AdCP entitlement terms for the life of the buy;
  • commits the authorized amount to the campaign ledger after the seller accepts or begins processing the buy; and
  • records dark alpha usage at zero billable amount.
Zero-billable alpha usage applies to the feature’s usage meter, not to media. The authorized media budget and zero-fee AdCP entitlement still govern the buy. Every caller budget is gross: the all-in amount authorized by the buyer. Pure AdCP access pins a zero fee, so gross and net are equal and the full media budget reaches the seller. Ordinary buyer clearing fees are not inherited on this path; advanced value is governed by entitlements/IUs. The seller never receives buyer terms. See Budgets and fees. The route is dual-keyed. Apostra campaign/media-buy identity drives contract, governance, and reporting records; the AdCP media-buy identity correlates the public task response and downstream seller object. get_media_buys and get_media_buy_delivery therefore remain addressable with the external buyer’s AdCP ID without making the seller’s ID the buyer-account primary key.

Current reporting behavior

get_media_buy_delivery is live read-through: Apostra calls the seller, returns the current delivery response, and opportunistically records realized spend against the pinned zero-fee entitlement ledger. The alpha does not promise a cached time series, change webhooks, scheduled delivery, or billing UI from that read.
Any future non-zero buyer-edge fee requires separate fee-inclusive delivery verification before rollout. The current alpha remains zero-fee and is not an invoice statement.

Current eligibility and limits

  • The buyer campaign spine follows the Seller’s Listing + Distribution package and is fail-closed. It is not enabled buyer by buyer, and there is no environment-variable bypass.
  • Every active Listing + Distribution storefront route creates the governed campaign, contract, operation, and ledger anchor before dispatch. This includes platform-composed storefronts and direct social/ad-platform ADAPTER storefronts. Wonderstruck ESA is the first live managed-agent proof; adapter routes also require a delegated, advertiser-mapped provider account.
  • Seller rejection and non-retryable dispatch failures mark the campaign as failed. A temporary source or adapter outage remains pending while Apostra retries for up to six hours, then becomes failed if the retry window ends without a successful dispatch.
  • The governed endpoint spine covers create_media_buy, supported update_media_buy fields, and cancellation. Update/cancel share the pinned policy, durable mutation journal, budget-delta caps, SCD2 activation, dark metering, ledger commitment sync, and ambiguous-outcome fence. Adding packages still returns a capability error.
  • Strict SDK lifecycle testing and source-specific end-to-end verification are both required before enabling another Seller’s distribution package.

Where the truth lives

AdCP specification

The protocol’s source of truth.

Connect your sales agent

The seller (supply) side — bring your AdCP agent in as a storefront source.

Get products across storefronts

Canonical AdCP-shaped retrieval with Apostra fan-out, screening, refinement, and purchase continuation.

Discover Products

The legacy-compatible grouped, browsable discovery-session projection.

Create a campaign

How a campaign spawns media buys across sellers.

Budgets and fees

Why the buyer budget is gross while the seller receives net media spend.

Campaigns on connected provider accounts

Tracked campaign mirrors — the opposite ownership direction.