brand.json does not grant account access, prove
domain ownership or authorise an agent to act for that brand. Organisation
names, login domains, users and roles are outside this setup flow.
Select the confirmed identity contract
For this workflow, sendidentityContract: "confirmed-v1" on every operator
identity save or advertiser brand correction. Keep it on the preview,
confirmation and retry calls. Replacing an existing identity in this contract
requires a preview and the person’s explicit confirmation; omitting the token
returns CONFLICT without saving.
This contract coexists with earlier v3 saves on the same tools. Calls using
only the earlier fields retain their existing behaviour, including one-call
Buyer operator and advertiser brand updates and the Seller’s
confirmOperatorDomainProfileReset check. They do not acquire a new preview
requirement. New setup integrations should select confirmed-v1 explicitly.
Supplying preview (including false), confirmationToken, or an advertiser
update’s publicBrand also opts into the confirmed contract. An unsupported
identityContract value is rejected; it does not fall back to an earlier save.
Find the account and its current identity
Calltools/list, then get_status with no accountId. Name the account ID
it returns as accountId on every call, and read status for that account. A
principal with several accounts must name the Buyer or Seller Account it
intends to configure. An Organisation-only account provides
orientation and support asks; it does not expose a separate identity editor.
For a Buyer,
operatorDomainSource: "legacy_customer_domain" identifies a
fallback from the account record. It is not a confirmation of the commercial identity.
domainProof reports the separately recorded verification and its source.
organization and aao describe the evidence used by readiness; inspect them
alongside locked.
Every Buyer get_status read looks the company up in the AgenticAdvertising.org
(AAO) registry, the same lookup the web app makes, and reports what it finds in
aao. The registry match never decides whether the company is verified: that
always comes from Apostra’s own domain evidence. The lookup has a short
deadline. When the registry is slow or fails, aao.status is unavailable and
the read still answers.
blockers lists exactly the setup checks that block buying and are not yet
complete, in checklist order: operator_identity, terms, account_standing,
payment_terms and billing_details. billing_details blocks while the
billing organization lacks a payer name, street address or country and the
buyer buys from a consolidated-billing seller. state and nextActions read
the same checks, so a blocker never appears in one and not the other.
Read the whole setup checklist and every seller
A Buyerget_status call can return the full checklist and one row per seller.
Send include: ["setup"]. Only a Buyer account accepts it: any other account
kind is refused with VALIDATION_ERROR naming include.
A seller’s
status is one of ready, seller_unavailable,
needs_connection, needs_account_selection, needs_advertiser_mapping,
needs_commercial_setup or platform_blocked.
Every action is a tool call, { "tool": "...", "arguments": { ... } }, or
null when there is nothing to call (the check is complete, or you cannot
manage the identity). It is the same shape as nextActions:
When
sellers.nextCursor is present, call get_status again with
include: ["setup"] and sellersCursor set to it for the next page. Send
sellersCursor only with include: ["setup"]. A cursor that is malformed,
stale because the seller list changed, or issued for another account is refused
with VALIDATION_ERROR naming sellersCursor; start again from the first page.
The text of the response lists every check and every seller on the page, each
with the tool to call, and ends with the sellersCursor for the next page.
For an advertiser, source: "saved_brand_link" means a saved branding snapshot.
It does not assert that a published document is available now. A Seller’s
identity.source: "storefront_config" likewise describes saved configuration.
manifestUrl: null means that this read cannot identify a published URL.
brandRef.domain is the saved brand domain as a lowercase host, without a
scheme, port or path. The advertiser’s brand field keeps the domain exactly
as it was saved. When no valid domain can be read from the saved value,
brandIdentity is null and the rest of the advertiser still loads.
Confirm the Buyer’s operator
Usesave_buyer_operator as an account administrator:
name is the account name, and its id is a
short code fixed when the identity is first saved:
- Omit the code and one is generated from the account name, for example
“DM - Restaurant Company Europe” becomes
dm-restaurant-company-europe. If another account under the same operator domain already holds it, a short numeric suffix is added (-2,-3). - To choose a shorter code, send AdCP
operator_unitwith theid, for example"operator_unit": { "id": "rce" }. Use something your company already uses. Never usedefault,globalorprimary: no account is the default, global or primary one, andsave_buyer_operatorrefuses those codes. - Saving again without a code keeps the saved one. The code never changes when the account is renamed.
get_status reports the saved unit as operatorIdentity.operator_unit, with
name taken from the buyer account name.
Join or leave the seller-history co-op
save_buyer_operator also carries sellerHistoryCoop, the account’s
membership in the seller-history co-op, how
an account takes part in the data co-op of Section 6.3 of the Terms of Service. It
starts false, and get_status reports the current value as
operatorIdentity.sellerHistoryCoop. The setting is part of the operator
identity because the operator that owns the account decides, so it takes the
same administrator permission.
To change only the membership, send the saved identity again unchanged with
sellerHistoryCoop set. Joining shares the account’s judged results as
aggregates, and leaving stops the account reading pooled history and sharing new
buys while what it already shared stays in the pool, so either change needs the
preview and confirmation below. The preview returns
preview.sellerHistoryCoop with the before and after values and a link to the
page that says what a member shares and gets. Show that to the person before
confirming.
Confirm an identity change
Before replacing an existing domain or unit code, send the proposed values withpreview: true. This explicitly requests action: "preview"
without saving. Show the person the before and after values, including the unit code the save
will use, and any loss of domain proof. After they confirm, repeat the same request without preview
and with the returned preview.confirmationToken.
If you did not name a code and another account under the same domain takes the
previewed one before you confirm, the save is refused with CONFLICT and saves
nothing. Preview again to see the new code, and confirm that one.
In confirmed-v1, an unconfirmed replacement that does not request a preview
returns isError: true with code: "CONFLICT" and saves nothing. A successful
preview is returned only when you explicitly request one. A caller that
attempts an unconfirmed replacement in this contract receives an error rather
than a successful-looking result that preserves the old identity.
The confirmation belongs to that account, current identity and requested
change. A stale or different confirmation fails with CONFLICT; read and
preview again. An identical retry with no new proof returns unchanged,
preserves confirmation timestamps and domain proof, and does not create a
second identity audit event.
A successful write returns a resource receipt and its audit ID.
Read get_status again to check the persisted domain, unit and proof. The
first advertiser binding locks the operator identity. A locked correction
requires support, rather than a different domain spelling or another account.
One change is still open on a locked identity. An identity saved before every
account had a unit can be locked by an advertiser while it has no unit:
get_status reports locked: true and operator_unit: null. Repeat
save_buyer_operator with the same operatorDomain to give it its unit, with an
optional operator_unit code. No preview is needed. This changes the key
sellers receive, so it is refused with CONFLICT while a seller account already
holds the key without a unit: a complete-key Seller account, or an external
seller account linked to one of this account’s advertisers. Request support
through save_ask to move those accounts onto the unit.
A verified account that still has no unit is not blocked from setup, but
get_status offers choosing its code as a recommended next action
(operator_unit, save_buyer_operator), because opening a seller account is
refused without one. The v2 readiness check shows the same step as
Choose account code on the operator_identity check, which stays complete.
New Seller connections can then set up the account. A different domain on a
locked identity still requires support.
Opening seller accounts needs an operating unit
Every Buyer account is its own operating unit. Before a seller account can be opened for this Buyer account, its operator identity must have anoperator_unit. This holds on every route that opens a company seller account
for a seller on complete-key accounts: AdCP sync_accounts, the v2 Buyer
account sync, automatic activation, and the support operation that requests
accounts for earlier per-user grants. An identity with no unit yet (saved before
every account had one) is refused with the correctable ACCOUNT_SETUP_REQUIRED
error and details.reason: "operator_unit_required". Nothing is created, and
the same request succeeds once the unit is set.
Set the unit with save_buyer_operator, as described above. If an advertiser
has locked the identity and seller accounts already use it, request support
through save_ask. Seller accounts that already exist are not changed.
Resolve and link public branding
In a Buyer Account, callsave_advertiser with only:
identity.source is brand_json or hosted only when
the brand publishes its own identity, which is what publicBrand: true
requires; null means the name and logo came from another source. The lookup
answers within about eight seconds. If it is still running at that point, the
call returns an unresolved card with pending: true; call it again shortly
and it picks up the same lookup. Media Company accounts that create their own
advertisers use the same call.
In a Seller Account, save_seller with only resolveBrand looks up the
Seller’s own public identity. That lookup requires a published brand.json or
an AAO-hosted brand identity and does not fall back to inferred branding.
Both lookups are read-only. A Seller Account’s brand name, logo and logo
background are refreshed from the same published identity when you save the
operator domain with save_seller, whether or not the domain changed. If the
brand has no published identity, or the lookup is slow or unavailable, the
domain save still succeeds. When the domain is unchanged, the stored brand is
left as it was. When the domain changes, any of the three that the new domain
does not publish is cleared with the rest of the previous domain’s profile,
so the old brand’s logo never stays beside the new domain. Previews
never write. Inspect resolved, identity.brandRef,
identity.source, name, logo and manifestUrl. identity.brandRef is an AdCP brand reference; it has
no brand_id when the domain has no house brand id, or when the published id
is not a valid AdCP brand id. An unavailable registry
returns an error; a missing
brand returns resolved: false and may supply a builder link.
To create a Buyer advertiser that requires public branding, call
save_advertiser:
identityContract; use that field only
when correcting the brand of an existing advertiser.
Keep the key and creation fields unchanged when retrying. A key reused for a
different request is rejected. The creation receipt includes its audit event
ID. Existing calls that omit publicBrand retain the normal brand-resolution
behaviour, which may include inferred branding.
If a brand-resolution source is rate-limited or temporarily unavailable while
you create an advertiser, save_advertiser returns SERVICE_UNAVAILABLE
instead of treating the brand as missing. Wait and retry the request; the
error includes retryAfter in seconds when the source supplied one. No
advertiser is created for that response. A missing-brand validation error
means every resolution source answered without data.
To correct an advertiser’s brand, supply identityContract: "confirmed-v1",
advertiserId, brand, preview: true, and optionally publicBrand: true.
An account administrator must review the returned preview and repeat the
request without preview and
with its top-level confirmationToken after the person confirms. An
unconfirmed replacement in this contract without preview: true returns
CONFLICT and saves nothing. A saved replacement returns
advertiser.identityChangeReceipt with its audit ID and brand-link IDs. Read
the advertiser again to verify brandIdentity.
If brandIdentity is null, the saved link cannot be inspected safely. Request
support with save_ask to repair that legacy record before replacing it.
An identical brand-only retry does not rewrite the link. With publicBrand: true, it also checks that public identity is currently available and returns
that lookup separately from the saved snapshot. It does not refresh the saved
name, logo or document contents.
v3 can resolve public branding and link an advertiser to a resolved brand.
It cannot create, edit or publish
brand.json, choose who owns an AAO-hosted
document, or refresh a saved snapshot in place. Use the domain publisher or
the returned external builder for document changes. These responses report
editingSupported: false. A public lookup is never domain proof.Change an advertiser’s brand after first use
An advertiser’s brand can still change after its first campaign or seller-account binding. The change applies to future buys only. The advertiser keeps its ID, name, campaigns, ledger, measurement, reporting, creatives, labels and tracking, because none of them are tied to the brand.- Buys already sent to a seller keep the seller account they were booked under. Updates, cancellation, delivery reporting and creative sync for those buys stay on that account until the buy ends.
- Draft and unsent buys go out under the new brand when they are sent.
- Sellers hold their accounts by brand. The first buy under the new brand at each seller creates or finds an account for that brand, and the seller decides on it separately: an approval for the earlier brand does not carry over. Accounts held under the earlier brand are not changed, revoked or deleted.
- Accounts you own on a platform, such as an ad account connected with OAuth, stay linked. Links to accounts a seller provisioned under the earlier brand are not used for buys under the new one.
- Brand data follows the new brand: the effective brand profile, creative sessions started afterwards and property-targeting defaults. A creative session, campaign creative or media buy that saved its brand earlier keeps what it saved. Looking an advertiser up by brand domain matches the current brand only.
- Currency and sandbox stay fixed. A different currency or sandbox state is a different advertiser.
- A brand an advertiser has used stays reserved to it for as long as the advertiser exists, archived included, even after its last buy on that brand ends. Another advertiser cannot be created on it or moved onto it, and the refusal names the advertiser that holds it. Deleting an advertiser releases its brands.
- Two exceptions to that reservation. An archived sandbox advertiser releases every brand it has used, the current one and earlier ones, so another sandbox advertiser can take one of them. Restoring the archived advertiser is then refused while another active sandbox advertiser holds one of those brands. An archived production advertiser keeps all of its brands.
- Changing back reuses earlier seller accounts where their links still exist. An advertiser holds one active link per seller agent. If a newer link at the same seller agent replaced the link a seller provisioned under the earlier brand, changing back does not restore it: that seller account may need to be found or provisioned again. Seller accounts themselves are not deleted.
brand.json, as they do for a
new advertiser.
Use the same confirmed flow as a correction: identityContract: "confirmed-v1", advertiserId, brand and preview: true, then the same
request without preview and with the confirmationToken after the person
confirms. Only an account administrator can make the change. For a brand
change, the preview also returns impact, so the person can see what the
change touches before confirming:
The token is bound to the brand link the preview was computed from and to its
complete impact, every buy and seller rather than only the ids the preview lists.
If the brand changed after the preview, or a buy was sent to a seller since, the
confirmation is refused as stale; preview again. A buy that was sent to a seller without a stored seller account would
otherwise look up its account under the new brand, so the change stores that
account first, from the brand it is still on, completed buys included. If a
live buy’s account cannot be worked out with certainty, for example because its
seller account is no longer linked to the advertiser, the change is refused and
the error names the buy. Nothing is changed in that case: fix what the error
says, then try again. A buy that has already ended (completed, canceled,
rejected or failed) and whose account cannot be worked out does not block the
change: it will not be updated or canceled again, so it is skipped and listed
in the advertiser’s audit history. A later delivery or status read for that
ended buy may then look its account up under the new brand.
An advertiser that a seller reaches through external self-serve access cannot
change its brand, because that access is pinned to the current brand and would
be retired for good. Create a new advertiser for the new brand instead. The
preview refuses it too.
An advertiser that a seller has on its advertiser roster under its current brand
also cannot change its brand yet. The seller’s roster matches the advertiser
by brand, so the entry would stop resolving and the seller would see the
advertiser as unresolved. The preview and the confirmation both refuse it. Create
a new advertiser for the new brand, or ask the seller to remove the roster entry
first.
The raw v2
PUT for an advertiser has no preview, so it keeps refusing a brand
change after first use. Use save_advertiser for this change.
Confirm the Seller’s operator and follow proof
Usesave_seller as an account administrator:
preview: true before any fields change. Review
the previous domain, proof and profile values, then repeat without preview
and with its top-level confirmationToken after the person confirms. An
unconfirmed replacement in this contract without preview: true returns
CONFLICT and saves nothing. Fields curated for the previous domain, including
description, channels, membership status, website, brand name, logo and logo
background, may be cleared. Description and channels can be supplied in the same request to
replace those values; the other legacy profile fields are not writable in v3.
An intervening identity or profile change requires a fresh preview. The older
confirmOperatorDomainProfileReset flag alone does not replace a confirmation
token in confirmed-v1; its earlier behaviour remains available to existing
callers as described above.
The save returns a receipt with its audit ID and, when present, the domain
review ID. Saving the domain uses the existing verification service and human
review queue. An already approved domain may verify immediately; otherwise the
current review is visible through:
operatorDomainVerified and identity.domainProof.review. The review
contains its ID, domain, status, request time, revision, decision time and ETA
when assigned. Poll this same read after the human responds. PENDING is not
proof; REJECTED requires a correction or support follow-up. Verification is
complete when the persisted domain matches the intended domain and
operatorDomainVerified is true. A repeated identical save does not duplicate
the review. Apart from the brand name, logo and logo background described
above, saving a Seller domain does not copy the public lookup’s document fields
into Seller configuration.
A domain-only save for the already saved domain returns unchanged; members
can make that request without administrator write permission or a proof-review
read. Actual domain changes require an administrator. Proof reads are
cancellable and have a bounded wait. If a transient read failure returns
SERVICE_UNAVAILABLE, retry get.
Request a person when proof or correction needs help
Show the person the proposed support request before filingsave_ask:
askId. In a Buyer or Seller Account, poll it with
get({ "kind": "ask", "id": "ASK_ID" }), follow the human’s instructions,
then re-read identity. A filed or resolved ask does not itself verify a domain.
Do not paste passwords, OAuth tokens or other secrets into chat.
If support approves a Buyer domain, repeat save_buyer_operator with the saved
domain to record that approval, then check get_status again. The
save checks approved evidence separately; repeating domain text without that
evidence cannot verify it.
A DNS TXT record check runs from the account’s domain settings, not from v3.
v3 can inspect the evidence and request human help. A Seller review with no current
record, a rejected review, or a locked identity correction can use the same
support path. Shared workflows covers ask
states and follow-up.