Tracked and managed
Connecting a seller account puts its campaigns on the platform in one of twomanagement states:
tracked— a campaign Interchange did not set up. On sync, the platform materializes a read-only projection of the seller’s own campaign and keeps it fresh: the shell is derived from the media buys underneath it and updates automatically as the seller changes things. Interchange supplies tenancy, identity, audit, governance configuration, and reporting access around it; it never acts on it.managed— a campaign authored or adopted through the platform spine. For separately write-enrolled accounts, Interchange forwards an explicit media-buy create or update and then reconciles from the provider response. Campaigns authored this way — including by an external AdCP client addressing one storefront — are managed from birth.
mode: "directed" — a deprecated wire
discriminator, not a product name, kept while the alpha cohort migrates to
management. A discovery campaign that happens to finish with one seller
remains an ordinary managed campaign; Interchange performed the selection.
Every campaign response carries one mode and one management state:
The directed invariant is one-to-one: the storefront campaign is one AdCP media buy
and one Interchange campaign shell. The shell’s name, status, flight, and budget are
projections of the buy, not independently editable values. The projection preserves
both media-buy identifiers: Interchange’s
media_buy_id and the storefront’s
adcp_media_buy_id.
This includes an external AdCP client calling one Interchange-hosted storefront. The
storefront manages execution; Interchange records the contract, governance, reporting,
and ledger envelope. The campaign still returns mode: "directed". With the
buyer/storefront campaign-spine enrollment active, later update_media_buy and
cancellation calls enter the same durable directed-write journal before the storefront
is called. Accepted package-budget changes activate a new SCD2 version; accepted
cancellation releases the governed exposure. Adding packages on this endpoint path is
not yet supported.
Every directed campaign also returns explicit provenance:
directed.provenance: "connected_account"includes connection/account ids, provider, mirror freshness, and the upstream media-buy id.directed.provenance: "storefront_endpoint"includes the addressed storefront, the external buyer’s media-buy id, and Interchange’s media-buy id. It does not expose mirror state because this path is not an account mirror.
Connect and subscribe an account
First connect the official adapter and map its discovered ad account to an advertiser. For an agent-driven setup, the complete sequence is:list_storefrontsto find the official adapter, thenconnect_storefrontto obtain the human OAuth handoff URL;list_storefront_connectionsandlist_storefront_connection_accountsto select a buyable account returned by that connection;list_storefront_connection_account_mappingsto read its mapping state, thenmap_storefront_connection_account_to_advertiserwith the advertiser ID if the selected account is not mapped to the intended advertiser. The connection/account path identifies an official adapter; do not copy its storefrontplatformIdintosourceId;subscribe_directed_campaignsto start its mirror.
sourceId is optional and the canonical provider is
derived from the connection. External AdCP storefront sources still pass their
inventory-source sourceId.
Subscription immediately attempts a mirror sync. The adapter’s
get_media_buys operation is called without media-buy IDs so the account can
be enumerated. Active campaigns are always included; terminal history is
backfilled for one year by default. Subsequent worker sweeps reconcile changes
every 15 minutes by default.
Broad account enumeration is not treated as proof that a previously mirrored
campaign was deleted. If a known upstream ID is omitted, Interchange reads that
ID through a bounded, resumable direct-read cycle before treating absence as
authoritative. A returned terminal campaign remains available as a canceled or
completed mirror, including its prior packages and latest revision; an empty
terminal package response does not erase package history. Already-terminal
local mirrors do not require repeated provider reads. If a direct response fails
or omits any requested ID, the sync reports ERROR and retains the last valid
mirror. The one-year boundary limits new historical campaigns; it does not
suppress a later terminal transition for a campaign already mirrored.
A refresh for one campaign prioritizes that campaign inside the bounded
direct-read cycle, so large connected accounts do not delay its readback behind
the background continuation cursor.
The subscribe request returns after the initial enumeration attempt. Its response
contains backfillStart, lastSyncedAt, lastSyncStatus, lastSyncError, and
the mirrored/retired/skipped counts. Read the same account-level state later
with GET /api/v2/buyer/storefront-connections/{connectionId}/accounts/{accountId}/directed-campaigns/subscription.
Campaign lists and the campaign widget show the mode, provider, mirror state,
and directed.lastSyncedAt for each mirror. Active mirrors appear in the
default campaign list (the live working set); mirrored history — completed,
paused, or canceled campaigns — requires an explicit status filter or
status=ALL; see Relationship rollup.
On a failed reconciliation, lastSyncError and the single sync.errors entry
use the fixed safe summary Connected provider account could not be synchronized. Use errorCode, errorField, errorReason, and upstreamCode
for any available machine-readable diagnosis. Provider messages, request IDs,
account IDs, and media-buy IDs are never returned in these error fields or
persisted as the subscription error.
backfillStart is the fixed metadata-history boundary, not a running progress
percentage or completion timestamp. Enrollment is durable even if enumeration is
unsupported or temporarily unhealthy; in that case the subscription reports status
ERROR, lastSyncError, and zero reconciliation counts while other supported directed
operations remain available. The one-year backfill does not pre-fetch delivery reports.
To stop mirroring, delete the subscription. Interchange pauses its sweep and
retires the local mirror shells; it does not delete campaigns from the connected
platform. Status and unsubscribe remain available if alpha exposure is removed.
Governance caps are stored with the subscription for directed writes. Phase 1
read enrollment does not grant write authority. Writes require the separate
directed-campaign-writes customer gate plus maxMediaBuyBudget,
maxAccountBudget, and currency; missing or inconsistent governance fails
before a provider call. These values are set by an Interchange operator after
pilot authorization. Ordinary buyer callers cannot set or raise them by
subscribing again.
Registered AdCP storefront sources
An enrolled buyer can use the same connected-account campaign surface with a registered third-party AdCP storefront source. First register source credentials, calllist_available_accounts, and map the returned account to an advertiser.
Then call connect_adcp_storefront to project that existing relationship into
the shared connection/account plane:
connectionId and connectionAccountId; use those values
with the normal subscribe, product, campaign, and delivery operations. Official
adapters dispatch through their delegated adapter credential. Registered AdCP
sources dispatch through the AdCP SDK and the mapped buyer account. Identity,
governance, mirroring, idempotency, metering, and reconciliation are shared.
Every active storefront can serve directed mode. Connection records the
seller’s reported tools as readiness evidence rather than requiring a separate seller
flag. Each call still fails closed if the source, pinned credential, connection
account, or advertiser mapping is no longer active. An unsupported operation is
reported for that operation without hiding the storefront’s other capabilities.
For registered sellers, a snapshot is complete only when every requested page
returns an explicit boolean pagination.has_more and every continuation cursor
advances. Every media buy must include a valid ISO currency; when it echoes an
account, that account_id must equal the pinned connection account. Invalid or
incomplete snapshots never retire existing mirrors. Sellers that omit the optional
account echo must prove account-scoped completeness before their snapshots can retire
existing mirrors.
Previously mirrored nonterminal IDs omitted by a broad snapshot are also read
directly in bounded, lease-renewed batches. A persisted cursor resumes large
accounts, and every direct batch must return the complete requested set before
the sync can proceed.
Budget and package updates require a provider revision observed by the latest sync;
revisionless sources remain eligible only for safe operations such as pause or cancel.
This alpha operation does not accept an arbitrary endpoint URL or raw secret.
The seller source must already be registered on an active storefront. Endpoint
ownership, seller identity, and adagents.json authorization must be established
before a URL can become a transacting relationship.
Alpha operation readiness
The service uses an adapter-neutral AdCP task contract. Readiness describes which
operations have passed live end-to-end verification; it is not a product allowlist.
List and inspect mirrors
Use the normal campaign surface and filter by mode:GET /api/v2/buyer/campaigns/{campaignId} returns the same campaign resource
as other modes, including the single mirrored media buy and its upstream
packages. Discovery-only operations such as execute and product auto-selection
always reject directed campaigns. In mirror-only enrollment, pause/reactivate and
ordinary field updates also reject. Buyers separately enrolled in the write alpha
must use the explicit mode: "directed" create or update branch described below;
unsupported provider mutations continue to fail closed.
To pull a fresh upstream snapshot without changing it, send
{ "mode": "directed", "refresh": true } to the normal campaign update
endpoint. This re-enumerates the subscribed account; it does not write to the
seller.
A brief is never valid for mode: "directed"; it is a validation error, not
an ignored field.
When the buyer starts with an advertiser brief, call
plan_directed_campaign
before create. The preflight returns controlled provider decisions or explicit
clarification requirements without changing the provider. Accept a ready
proposal, then pass its exact reference on an assisted create. The subsequent
create still contains only reviewed, explicit AdCP fields and never accepts a
hidden brief.
Relationship rollup
Scale lives in the relationship, not the campaign list. Tracked campaigns never appear as ambient rows; the connected-account relationship carries the rollup and drill-in enumerates on demand. Each account onGET /api/v2/buyer/storefront-connections/{connectionId}/accounts
returns an optional campaignRollup object:
Spend is seller-reported net in the account currency: directed buys dispatch the
full media budget zero-fee, so net equals the buyer’s budget. Rollups are never
summed across currencies — the rollup is per account, and it is omitted when the
account currency is unknown or the account has no live campaign mirrors.
The connection rows on
GET /api/v2/buyer/storefront-connections carry counts
only: campaignsTracked and campaignsActive across the connection’s
accounts. Money never rolls up to the connection level, because accounts on one
connection can settle in different currencies.
Create and update through the connection
For an explicit buyer-authored plan, first calllist_directed_campaign_products with the mapped connection/account path and
advertiserId query. It reads the provider’s no-brief product catalogue and
returns the exact product_id and pricing_option_id values accepted by that
account. Do not use ordinary discovery output from another seller and do not
invent provider IDs.
get_products discovery, including when Murph uses it to
explore campaign options, is a separate read-only path. For a non-empty brief,
it returns runnable recommendations only when the buyer affirmatively names one
outcome. Otherwise Murph asks the buyer to choose reach, video views, traffic,
conversions, app installs, or lead generation and returns no products or
proposal. Reply with an explicit choice such as Objective: traffic and retry
the Murph request. This does not enable TikTok in the named
plan_directed_campaign operation, which currently supports Meta only, and it
does not authorize a provider write.
Create a directed campaign through the normal campaign endpoint. The request is
an explicit AdCP media buy: there is no brief, discovery session, or execute
step. connectionId, accountId, idempotencyKey, and currency live at the
campaign-request root so account routing, replay protection, and governance
cannot be overridden inside mediaBuy.
Every create records decision provenance. Use decisionMode: "raw" for an
explicit buyer-authored pass-through; omission remains backward-compatible and
also defaults to raw. Raw writes cannot claim Scope3 brief strategy. A Meta
brief-assisted create instead uses decisionMode: "assisted"
and the exact accepted reference from
accept_directed_campaign_plan.
paused: true and rejects creative assignments or
inline creatives server-side until the upstream object, mirror, governance, and
delivery reads are verified. Use future-dated campaigns as an additional
operating precaution.
TikTok packages can include canonical AdCP catalogs[] entries of type
offering or job; the adapter expands their items into provider-native
promoted offerings. Other catalog types return CAPABILITY_NOT_SUPPORTED
before the provider write begins.
Every TikTok package must include a non-empty
targeting_overlay.geo_countries array using a country supported by the
provider catalog. Interchange converts those ISO country codes to TikTok
locations and uses the explicit PLACEMENT_TIKTOK provider placement; missing
geo fails before the provider write begins. Interchange disables TikTok’s
automatic Search Results toggle and does not select an account-specific
subplacement. Lemon8, Search Feed, Pangle, and Global App Bundle are not
advertised as writable inventory. TikTok for Web, Profile Feed, and Following
Feed are not separate API selections in this flow.
The community catalog keeps the stable buyer-facing placement ID
tiktok_for_you_feed; Interchange maps it to the provider’s
PLACEMENT_TIKTOK enum internally.
TikTok Reach packages also require an explicit
targeting_overlay.frequency_cap. max_impressions must be from 1 through
1000, and the window.interval must be from 1 through 30 with unit: "days".
Interchange validates those provider bounds before creating anything in
TikTok and forwards the buyer-selected cap unchanged.
currency must match both the subscription governance currency and
mediaBuy.total_budget.currency. The total and package budgets must stay under
the stored per-buy and authored-account caps. TikTok’s campaign minimum applies
to the net media amount after fees; see
Provider minimums apply after fees.
Meta packages may use targeting_overlay.geo_regions with ISO 3166-2 codes such
as AU-NSW. Interchange resolves each subdivision to Meta’s native region ID
before creating the campaign and returns the canonical ISO code on readback. If
Meta cannot resolve exactly one region in the requested country, creation stops
before any provider campaign is created. When a package includes both a region
and its parent country, the region is authoritative; Interchange removes that
parent country from Meta targeting so delivery is not broadened nationally.
For Meta, every finite-flight package is written as an authoritative
lifetime_budget using the selected ad account’s ISO 4217 minor unit. A native
Meta campaign that exposes only daily_budget cannot be interpreted as total
exposure, so mirror and update operations fail closed until it has an
authoritative lifetime budget. Values that cannot be represented exactly in the
currency’s minor unit are rejected before Meta creates a campaign. A controlled
Meta update may decrease each identified package budget under the latest
provider-derived revision; one package cannot increase even if another package
decreases by the same amount. Schedule changes, resume, and package additions
remain unavailable during this canary stage.
Meta packages may reference stable creative_ids that were already synced to
the selected account on raw writes. Assisted decision-plan v1 rejects inline
creative execution; creative attachment remains unavailable for assisted v1
until the separate sync/assignment flow lands. A sync requires the legacy Meta format_id returned by
list_creative_formats. Scope3 auto-selects the only Facebook Page authorized
for the selected ad account; when several are authorized, provide one of the
named page_id choices returned by zero-write validation. When needed, the
validation message tells you the next ext.meta_page_selection_offset to send.
An explicit page_id or
optional instagram_actor_id must belong to the selected account and is
validated before provider writes. Single-image and video assets must be reachable at public
HTTPS URLs and satisfy the declared format’s roles, file types, dimensions,
aspect ratio, duration, and size constraints. Interchange validates the supplied
roles and metadata before writing. Image downloads additionally pin public DNS,
reject redirects, cap bytes, and verify JPEG/PNG content; Meta performs final
provider-side processing. Interchange does not ingest a host-local file or
upload assets from an MCP host; host the asset first. Carousel
child attachments, native localization/translation, and sync-time assignment or
delete mutations are not supported. Creative-bearing creates remain paused.
Interchange reports success only after Meta reads the ad back from the selected
account, campaign, and ad set with the requested creative and paused status. A
pending review status is still not a claim that the ad is launch-ready or that
no Ads Manager follow-up will be required; that claim requires affirmative
provider approval for the same ad.
Every Meta package must also include explicit geographic targeting. Country
values are normalized as ISO 3166-1 alpha-2 codes (UK is accepted as an alias
for GB); a missing country, invalid code, or postal area without its own
country fails before Meta creates a campaign. Region strings, free-form
interest themes, and custom-audience signals are not applied until they can be
resolved and round-tripped without losing provider targeting semantics.
After creating a paused ad set, Interchange reads Meta’s stored targeting back
and requires exact normalized country, region, city, and postal equality before
reporting success. A mismatch deletes the campaign and returns an error.
Campaign package responses expose representable geography and device choices as
targetingOverlay; providerTargeting carries the normalized provider country,
region, city, postal, age, and gender readback. For a package created through
Interchange, requestedTargeting retains the canonical request and
targetingMatchesRequest reports whether the current provider-normalized value
still equals it. This makes a later Ads Manager edit visible without rewriting
the original request. Provider-authored campaigns created outside Interchange
do not receive an invented request, so those two fields are omitted.
When mirroring an existing Meta account, Interchange also checks for native
targeting constraints that the directed-campaign projection cannot represent
without losing meaning. A nonempty excluded geography, locale, interest,
behavior, included or excluded custom audience, publisher platform, or
Facebook/Instagram placement makes the account sync fail instead of returning a
partial package. The previous complete mirror remains available and the
subscription reports an error until every native constraint can be represented
or is removed. City-radius or distance-unit proximity and unknown Meta targeting
dimensions fail the same way.
Meta does not offer an atomic compare-and-swap for these fields. Interchange
assigns a durable monotonic revision whenever the provider campaign or ad-set
state fingerprint changes, including multiple changes in the same second. The
token remains an optimistic preflight: a native edit after the read and before
the write can still race it. Run the no-spend canary only in its dedicated
account, refresh before operator changes, and treat a revision conflict as a
requirement to reread rather than retry blindly.
Meta directed writes begin a new durable revision domain with this capability.
Revisions are opaque: always use the value from the latest read, never compare
their magnitude or reuse an experimental token observed before Meta directed
writes became available.
Update the same object through the normal campaign update endpoint. The URL
provides both the local campaign identity and its upstream media-buy identity;
do not repeat account, media_buy_id, idempotency_key, or revision inside
mediaBuy.
campaign plus a
directedWrite block whose status is CONFIRMED. If the provider accepted the
operation but the mirror cannot yet prove it, the endpoint returns 202 Accepted
with the write result’s status AWAITING_CONFIRMATION and a possibly null
campaignId; poll get_campaign or run the read-only refresh branch. Replaying
the same idempotency key and payload returns the recorded result. Reusing the key
for a different payload is a conflict, and an ambiguous provider outcome is never
automatically dispatched a second time.
Connected-account responses keep the two identifiers separate:
directed.upstreamMediaBuyId is the platform’s campaign identifier and
directed.mediaBuyId is Interchange’s internal media-buy identifier used by the
embedded mediaBuys[] entry. The embedded media buy also returns its
provider-derived revision and currently callable validActions; do not compare
the internal identifier to the upstream identifier.
If the seller accepts an update but Interchange cannot finish the local campaign
projection, the write is recorded as ambiguous rather than left dispatching. New
mutations stay blocked until that accepted result is reconciled; the seller request is
not sent again automatically.
Some platforms, including TikTok, omit a deleted campaign from subsequent complete
account snapshots instead of returning a terminal row. When that authoritative
absence follows a pending cancellation for the known mirror, Interchange confirms
the operation, records the projected buy as canceled, and keeps its terminal shell
readable so the response or an identical idempotent retry can report CONFIRMED.
The same absence never confirms an unrelated write. After cancellation, reuse only
the original idempotency key to read its result; a new mutation is rejected before
the provider is called. The retained campaign shell reports status: CANCELED, as does
its projected media buy.
The sync sweep marks any write left dispatching for more than five minutes as
ambiguous. Updates can still reconcile through their known upstream ID. For a
create with no returned upstream ID, inspect the connected account before taking
any further action and do not retry with a new idempotency key.
Product enumeration and directed create/update usage are recorded as dark,
platform-borne usage with zero billable amount during the alpha. Each provider
dispatch is counted once; a later async confirmation does not add a second usage
event.
TikTok updates use the latest provider-derived revision to reject a stale write
before changing the platform. Supported changes are campaign pause/resume,
package budget, bid, end time, and media-buy cancellation. Package cancellation,
new packages, start-time changes, and other package fields remain unsupported.
Interchange bounds each request to 20 package mutations, pauses and verifies an
enabled campaign before child writes, and reads the complete provider hierarchy
back before reporting success. Reversible partial failures are restored to the
pre-write provider values. If that restoration cannot be verified, Interchange
makes one final verified disable attempt and reports an unrecovered partial
update; the error states when provider containment itself cannot be verified and
manual reconciliation is required.
TikTok creation also fails closed at each provider boundary. Interchange verifies
that the campaign, every ad group, and every ad belong to the selected advertiser,
have the expected parent, and remain paused before activation. It activates
children before the campaign, validating each response identity and enabled-state
readback before progressing. If activation or later readback fails, cleanup never
mutates an ownership-unconfirmed object. For an owned campaign, cleanup first
disables the exact campaign and confirms its paused readback before requesting
deletion. The result is reported as rolled back only after TikTok returns terminal
deletion. If deletion cannot be confirmed after the pause is confirmed, the result
is contained_paused and requires operator reconciliation without being marked
possibly active. If the pause itself cannot be confirmed, the result remains
possibly active and requires reconciliation. Offering-based TikTok packages can
currently be created only while paused because the adapter cannot yet materialize
offering assets as runnable ads; use creative_assignments for delivery-ready
packages.
For every new ad group, Interchange also reads back placement_type,
placements, tiktok_subplacements, and search_result_enabled. The campaign
is deleted while paused if TikTok broadens, filters, omits, or changes that
placement contract. Provider-native campaigns that use automatic placement,
partner inventory, allowlisted subplacements, or Search Results remain visible
in account inventory but are read-only through Interchange.
Cancellation is terminal only after a bounded full-hierarchy poll confirms
TikTok campaign deletion. This path remains in live-canary readiness rather
than Green production evidence.
Read delivery through the seller
Delivery is read through live from the connected platform’sget_media_buy_delivery operation:
startDate is bounded to the subscription’s one-year
backfill window and endDate defaults to yesterday because same-day platform
reporting is commonly incomplete.
Every delivery request calls the connected platform. Interchange returns that
response to the caller but does not persist or later serve the raw delivery
payload or its time series in Phase 1. It persists only the latest aggregate
media-buy and package metrics together with the exact requested
windowStart/windowEnd and refresh time, so the campaign projection can label
what period its snapshot represents. Subscription and the 15-minute metadata
sweep do not pull delivery automatically.
Provider reporting limits are handled inside the adapter. TikTok daily ranges
longer than 30 days are read as consecutive windows and combined into one
delivery response, so the caller can request the full one-year window.
Snap delivery verifies the selected account and its ISO currency, enumerates
the complete ad-squad inventory, and reconciles package metrics to the campaign
report for the exact requested window. Empty packages remain visible at zero;
malformed, partial, duplicate, cross-campaign, or non-reconciling evidence
fails the read rather than returning a partial snapshot.
Errors and recovery
FEATURE_NOT_ENABLEDmeans the customer is not enrolled in the requested read or write alpha. Mirror access does not imply write access. There is no environment-variable or direct-adapter bypass.- If subscribe reports that the account is not mapped, re-list the official adapter connection accounts and mappings, map the account to the intended advertiser, then retry with only IDs returned by those list operations.
- If
lastSyncStatusisERROR, showlastSyncErrorandlastSyncedAtas the time of that failed attempt. A malformed or partial upstream snapshot fails closed and retains the previous mirrors. Reconnect first for authentication errors; otherwise retry a read-only refresh after the provider recovers. - A successful sync with
mirrored: 0is an empty eligible metadata window, not a failed reporting backfill.
Alpha boundaries
The directed-campaign alpha includes account subscription, campaign mirroring, a one-year historical window, delivery read-through, and separately enrolled create/update canaries. It does not include:- buyer webhooks for upstream changes;
- including mirrors in managed scheduled-delivery exports;
- public website positioning or general availability.