These workflows require a Seller Account. In v3, “Seller Account” is the
operator workspace; a Storefront is the buyer-facing selling surface it
operates.
Inspect seller activity
Open Activity under Operate in Storefront, in the left navigation. The destination is available while the Seller Account is being set up and after it goes live. It shows the account’s Calls and Changes without sending a new chat message.Add a Library document from Apostra chat
In Apostra chat, attach a PDF, PowerPoint, Word document, Excel spreadsheet, or CSV and ask the assistant to add it to the Library. A document larger than the chat attachment limit uploads directly from the browser through the same private-storage flow as the Library Page. Each document can be up to 100 MB and arrives as its own Draft. If a document exceeds 100 MB, choose a smaller file and try again. Open the Draft in the Library to review and save it. A file that names another publisher or an older brand still arrives as a Draft; the assistant records a review note there instead of declining the upload.Operate sponsored self-serve client campaigns
When a client operates a self-serve advertiser sponsored by your Seller Account, select it in Managed campaigns and open Campaigns. You can edit the same campaign fields that the client can edit, including campaign details, flight, budget, and lifecycle actions available in Campaigns. The client remains the campaign owner. Each change is recorded in the campaign’s history and your Seller activity asEdited by <your seller organization> (operator), so both sides can see that your organization made
the edit. This authority applies only to that active sponsored client and its
linked Storefront; it does not apply to external, delegated, or System
advertisers. System advertisers remain read-only.
Give a client access to an advertiser
To let a person at a client company run campaigns on an advertiser in your Seller Account, read the advertiser withget({"kind":"advertiser_access","advertiserId":"..."}). It lists the person
with access, what you can do with them now (availableActions), and whether
you can invite someone (invite). An agent does not change client access: the
changes are made on the advertiser’s Advertiser settings page, so send the
person there to invite, approve, suspend, reactivate, or revoke someone, or to
change their role. Only an organization administrator signed in directly can
make these changes, and the read is the only source of what is allowed. See
Invite and manage a client’s access.
Begin with readiness
Callget_status before changing configuration. Its blockers explain why work
is stopped before a buyer request reaches an inventory source and provide a
typed tool or Page for the next action.
Do not infer demand from source traffic alone. A Seller Account that is not
ready may correctly have no source calls because work stops earlier.
Confirm a Sales Agent product mode
Usesave_agent to confirm or correct how a Sales Agent supplies products.
Send the Agent ID, one product mode
(WHOLESALE, COMPOSING, or BOTH), and a reason:
sourceId. Supplying sourceId for an owner confirmation
is rejected rather than changing the binding or Agent declaration unexpectedly.
An administrator of a Seller Account may
instead correct only that Seller’s own bound Source when the Agent is
unclaimed. sourceId is optional when exactly one Source is bound. If several
Sources use the same Agent, the refusal lists their Source and connection IDs;
repeat the call with the exact sourceId returned by the inventory-source
read. This prevents a correction from selecting another Source. That correction
does not affect any other Seller. Once an Agent is claimed, its owner controls
the declaration; Seller requests are refused and direct the caller to the
owner. BOTH is valid for a Seller’s binding; its existing product-path
selection remains the binding’s lane selector. If a previous seller correction
selected one mode, changing it to BOTH keeps that mode as the lane selection.
BOTH is refused when the binding has no selection to retain.
Read and configure Seller identity
The Seller Account is a singleton, so read it without an ID:configuredCapabilities— the declaration you saved; andcapabilities— what buyers can use after source topology and approval policy are applied.
capabilitiesLocked and capabilityDerivation
explain why. save_seller writes the declaration under the same name the read
returns it, configuredCapabilities; the effective capabilities are derived
and cannot be saved. Send capability patches rather than replacing the whole
set:
setupIntent describes how the Seller Account is intended to connect. It does
not apply a capability preset or change the capability flags. Declare each
supported capability explicitly through configuredCapabilities; then read the
Seller again to see the effective values derived from source topology and
policy.
Changing operatorDomain may invalidate profile fields curated for the old
identity. If populated fields would be cleared, the tool refuses the write and
requires confirmOperatorDomainProfileReset: true after you review the impact.
Edit the listing
The listing is what buyers see on Apostra and in every channel: the description, the channels and countries you accept, a one-line subtitle, and your website, support, privacy policy and terms of service links. The ChatGPT app listing and its submission package read these values; they are not entered separately per channel. Send only the fields you are changing insave_seller’s listing:
null to clear any of these fields; a cleared
website falls back to the one in your brand.json. ChatGPT shows the first 30 characters of the subtitle. Read the
values back with:
Admit an external buyer and inspect delivery
The buyer declares its Advertiser footprint before it connects.brandCountries
and channels are the countries and channels the Seller listing is qualified
against; they are not display-only labels. The buyer then uses
save_connection to connect that Advertiser to the selected Seller.
relationshipState, the status the API decides for
it: needs_review, waiting_external, buying, rejected or new, who owes
the next step, and the first item that needs the Seller. Follow a returned
relationship with get to obtain the current admission state and, when it is
awaiting the Seller, the exact intake ID and version:
save_seller call, preserving the version from that read:
decision: "decline" with a non-empty message to refuse an intake.
External-authority and delegated requests are reported as such and cannot be
decided by this Seller. A successful seller decision can still await
ready-to-invoice evidence before an active grant is available; re-read the
relationship rather than assuming that it is immediately eligible.
Open the existing relationship-scoped Media Buys Page to inspect the admitted
buyer’s execution and delivery:
sellerAccountMappingId instead of accountRelationshipId; the
two are never combined. It is checked the same way, and a foreign or unknown
account opens nothing. sellerAccountMappingId is the mapping’s own ID, not a
seller’s Account id. Its earlier name, sellerAccountId, still works and is
being retired.
Review, source mapping and information requests for the buyer’s account happen
in the Buyer Account Mapping Page.
Add and diagnose inventory
Usesave_inventory_source for typed source configuration. For an existing
modular source, pass moduleConfig with its seller-local moduleInstanceId to
update one module’s non-secret configuration, sending config as a list of
{ key, value } entries whose keys are the module contract’s dotted field keys;
save source fields in a separate call so each write has an unambiguous outcome.
The object form of config is deprecated. Credentials and
OAuth grants stay in the relevant Page rather than model-visible arguments.
To connect to a partner’s shared sales agent, pass only its agentId. The call
opens the partner connection form, where
you enter the partner’s fields and credentials, and creates nothing itself.
To connect an ad server, pass only adServer with its type. The call opens
the Connect ad server form and creates nothing itself; for Google Ad Manager it
also returns the service account email to add to your network. See
Connecting an ad server.
Read a source with connection and diagnostic evidence:
moduleRoster before the detailed
modularReadiness projection. moduleRoster.modules is the complete attached
module list and carries each module’s instance ID, kind, name, contract ID, and
status; supportedStages lists the lifecycle stages covered by the attached
modules. A null contract ID means the contract could not be resolved — it does
not mean the module is absent. The detailed readiness projection can be large,
so treat an explicit truncation notice as evidence that only a prefix of that
detail is visible.
Testing an ad-server connection
get contacts no ad server. Every connection fact it returns — the adapter
block, health, sync state — is state recorded earlier, and none of it is
evidence that the connection works right now.
For an ad-server-backed source the read makes that explicit in adServerProbe:
when the connection was last probed, what that probe recorded, and null when
nobody has ever probed it. A connection reporting connected with
adServerProbe.lastTestedAt of null has never been checked against the live
ad server.
Probing is a live call with a real cost, so it belongs to a person rather than
to an agent acting alone. Open ad_server_diagnostics and run Test
connection there:
GET /agencies/{agencyId}). A source whose agency currency has never been
read is withheld from buyers as Not buyable. Apostra runs that probe for
you within fifteen minutes, and Test connection starts it now. The agency’s
answer decides what the source can sell in; the currency typed on the
connection is never treated as proof. Three outcomes need different action:
- The probe fails. The Page reports the failure: AdsWizz rejected the API key, refused the agency, or could not be reached. Reissue the key or fix the agency it is scoped to. Nothing about the currency changes until the probe passes.
- The probe passes but the agency names no currency. The key can list advertisers but cannot read the agency record. Ask AdsWizz to grant read access to it, and Apostra’s next probe records the currency.
- The agency reports a currency your storefront does not settle. Change the storefront’s payout currency, or use an agency that books a supported one. Apostra never converts between them.
sourceId and its
ad-server connection id (managedSa.connectionId, used as esaId on Pages).
get accepts either and tells you which it resolved from, so holding one is
never a dead end.
Common Pages include:
connect_ad_serverfor credentials and OAuth;connect_sales_agentto choose one of your organization’s sales agents or a certified partner’s to connect;ad_server_sourcefor connection management;ad_server_diagnosticsfor sync history, refresh, and the connection test; andsource_diagnosticsfor call-level investigation; see Source diagnostics Page.
modular_source_setup, modular_inventory_source and
modular_inventory_feed open its setup, readiness workspace and feed commit;
see Modular source Pages.
Create products and declare Prebid
Create, change, archive and delete a wholesale product on an ad-server source withsave_wholesale_product. Declare that the Scope3 Prebid module runs on a
product with its prebidIntegrationActive field. See
Create and manage seller products and
Declare the Scope3 Prebid module on a product.
Save instructions for a buyer and brand
save_advertiser_instructions writes notes, a country scope and a discount for
one operator and brand pair. See
Seller buyer instructions.
Manage publisher coverage
Coverage separates declarations from evidence:- declared domains and properties are operator claims;
- discovered properties remain evidence from publisher sources; and
- authorization is a verdict derived from the current roster and proof.
status, the one-word roll-up of whether Apostra may sell
it: authorized, unauthorized, pending (not resolved yet) or error (the
latest adagents.json lookup failed). It is null when the lookup found no
conclusive evidence either way, such as a missing or invalid adagents.json. It
is the same value the setup checklist shows for the domain, so you do not need to
work it out from adagentsStatus and authorizationStatus.
Use save_coverage to add or remove one domain or property without rebuilding
the entire roster. Domain declarations in one call are applied before property
claims beneath them, so a new domain and property may be declared together.
Removing publisher-origin evidence is refused; retract only claims you own.
Each property is an AdCP Property, so it keeps AdCP’s field names on both the
save and the read: property_id, property_type, name, identifiers, tags
and, on a claim, publisher_domain. The read adds three fields of ours to the
same object: property_key, the key a retraction names; provenance; and
authorization.
domains on save_coverage is a
list of domain strings, the read’s domains[].domain, and replaces every domain
you declared. Claims in declareProperties are a flat list naming their
publisher_domain; the read nests properties under domains[].properties. The
read also lists domains the platform found rather than you declared; a
domains save does not remove those.
Add and inspect seller material
Sellers can select Add material at the top of the Library page to upload a supported file directly. The Library uses thesave_material upload lifecycle
below and then reads the finished Material back to show its units, pricing
marks, and reusable toggles. The other paths remain available: drop material
into Apostra chat, or upload it while closing a specific Library request.
Use save_material to register teaching material without requiring a first-party
Page. Supported source kinds are url, site, upload, crawl_manifest,
inline, and history. For an upload, provide sha256 with the declared byte
count and media type. The first save returns an authorized, expiring PUT URL
and an immutable assetRef. Upload the exact bytes with the returned headers,
then call save_material again with the materialId, its expectedRevision, a
new clientRequestId, and a source that carries that assetRef; it cannot be
attached to a different Material or a later refresh. Each refresh creates a new
source revision and retains the prior original and renditions.
save_material states what you want in its fields, and one call declares one
change. With no materialId it creates a Material. With a materialId it takes
one of these, each in its own call:
source, withexpectedRevisionandclientRequestId, adds a source revision.metadataandlabelsmay travel with it.metadata, withexpectedRevision, edits the Material.labelsneedmetadatain the same call.isArchived: truearchives the Material andisArchived: falserestores it.candidateDecisionsrecords decisions on extracted candidates.unitReusablemarks slides, pages, or sheets reusable.reprocess: { "sourceRevision": N }, withclientRequestId, asks for a new rendition of that source revision.commitPreviewapplies a previewed rate card or avails sheet.
create uses bytes that already belong to a non-archived Material
in the same Seller Account, it returns that Material rather than creating a
duplicate. The response has alreadyExisted: true with its materialName and
materialState. Matching a Live Material changes nothing. Matching a Draft
can apply only the request’s explicit displayName or reviewNote; a fallback
attachment filename never renames the Draft. This check is isolated to the
Seller Account, so another account can save the same bytes.
The processing result has two complementary forms. Rendition blocks and visual
assets preserve extracted text, page or slide structure, images, crops,
captions, layout hints, and source locators for later human presentation. Typed
candidates propose structured changes for their destination, such as a format,
signal, business rule, or seller identity. The agent retrieves prose passages
directly when it composes a response; prices never come from prose.
reusable and whether it containsPricing. Every unit starts
with reusable: false. Only a unit from an uploaded deck or one-sheet can be
made reusable; sites, URLs, crawl manifests, spreadsheets, and dictated
Material do not yield reusable units.
Use save_material with the materialId and unitReusable, a list of
{ "unitId": "UNIT_ID", "reusable": true } entries, after reading the Material
and the units you intend to mark. Entries are applied in order, and an entry
that fails names its position; the entries before it stay recorded. A unit that
containsPricing cannot be marked reusable until its price figure is replaced
with a rate-card value. A reusable unit keeps layout figures, while its prices
resolve from the rate card.
Review and confirm extracted candidates
Extraction returns review candidates; it never changes live selling state. Each candidate names its source Material and revision, stable candidate ID, exact evidence locator and digest, relevance and confidentiality scope, and canonical owner. It contains either a schema-validproposedMutation or an unwritable
reason.
Playbook instructions use save_playbook; AI Business Rules use
save_business_rules; formats use save_wholesale_product; signals use
save_signal; and Seller identity uses save_seller. Do not move a proposal
to another tool or remove its materialCandidate provenance. Prose passages
are cited directly from the current document revision rather than accepted as
candidates. Visual assets pass
a separate rights, confidentiality, expiry, media-type, and accessibility gate;
accepting a text candidate does not authorize its images.
Playbook and AI Business Rules content saves replace their complete active
documents. Extraction therefore returns these suggestions as unwritable when
it does not have the current complete destination document. Merge the evidence
with the current document first, then save the complete result through the
named canonical tool; do not confirm a one-line replacement.
For example, a Seller-description candidate can return:
destinationOwner and the primary evidence locator remain available for
clients built against the first v3 Material response. New clients should use
proposedOwner and evidenceItems for the complete typed routing and evidence
envelope.
After a person or authorized agent explicitly confirms the proposal, call the
named tool with those exact arguments. A successful save adds
materialReceipt to the response. Reading the Material again projects the
bounded receipt with the destination tool and destination identity. Replaying
the same confirmation returns that receipt without writing twice. A conflicting
or concurrent decision returns an error and makes no additional write. If the
recorded destination save succeeds but receipt projection is interrupted,
retrying the same confirmation resumes the receipt projection without running
the destination save again. While a destination call is running, its dispatch
fence is heartbeated and cannot be reassigned. If the process ends without a
usable destination acknowledgment, the result becomes indeterminate: do not
repeat the save. Verify the canonical destination and reconcile that application.
Client cancellation after dispatch does not abandon the owner write; its
heartbeat is bounded and stops when the owner settles or the application is
marked indeterminate. save_material does not change a typed proposal.
Confirm it by calling its tool with exactly the proposed arguments. To save
different values, call the tool without materialCandidate and reject the
candidate.
For multi-part Playbook and AI Business Rules proposals, every requested part must
be acknowledged before the candidate becomes accepted. If only some parts are
saved, materialPartialReceipt records the completed and pending parts with the
owner response and its applicationId. Do not replay the full proposal. Resume
only the pending parts through the same typed owner, then call save_material
with the materialId and a candidateDecisions entry that accepts that
candidate at its sourceRevision, with the verified canonical record still in
place. Accepting a destination-owned candidate whose typed save already ran
reconciles its application: the server finds it, re-reads the named typed
owner, and does not accept a caller-supplied destination, dispatch, or repeat
an owner save. The entry reports reconciled: true. Exact concurrent or
replayed reconciliation calls return the same receipt.
A late owner acknowledgment is stored as destination_saved before Material
projection. Projection failures retain bounded retry evidence and resume
without calling the owner again.
Use save_material with the materialId and candidateDecisions, each entry a
candidateId, the sourceRevision that contains it, and a decision whose
action is accept, reject, correct, or withdraw, to record review
outcomes without applying a destination-owned proposal. A correct decision
carries correction.correctedContent, the replacement title and summary,
and applies to Material-owned candidates only. Decisions are recorded in order
and are idempotent per candidate.
An MCP call that still sends correction.proposedMutation keeps working until
the next ChatGPT re-approval; it is deprecated. It is not in the published
schema.
A source may carry supplied structure. Each entry of
derivedStructure.nodes[].provenance is a { "key", "value" } pair (at most 25
per node, names unique, key up to 100 characters, value text up to 500).
Each renditionRefs[].completeness is an object with any of text,
structure, tables, visuals and styles, each complete, degraded,
unsupported or failed; a facet you leave out is not stated. get returns a
source’s provenance as stored, a map from name to text, and completeness as
stored. To save a node back, send its provenance as entries. An MCP call that
still sends provenance as a map, or a facet outside the five, keeps working
until the next ChatGPT re-approval; it is deprecated.
Material states are needs_upload, queued, processing, ready,
partial, or failed. partial and failed include diagnostics and a safe
next step. Unsupported facets are reported as unsupported; bounded or lossy
facets are degraded. Neither state is presented as complete or ready.
The Product Marketing Page is a v3 Material client. It lists Material with
search({ "kind": "material" }), reads each visible record with
get({ "kind": "material", "id": "MATERIAL_ID" }), and records
Material-owned candidate decisions with save_material. Sellers can select a
supported file in the Page; it verifies the type, size, and SHA-256 digest
locally, reserves seller-private storage, uploads directly with the expiring
PUT capability, and finalizes the exact reservation by adding its source revision.
The signed URL and headers are used transiently by the widget and are never
rendered, written to model context, or retained as retry state. Supported Page
uploads are PDF, PowerPoint (.pptx), Excel (.xlsx), CSV, PNG, JPEG, GIF, and
WebP files up to 100 MB.
Each Material card shows the canonical processing state and whether original,
extraction-diagnostic, and visual-asset extensions are available. Visual
inspection loads only bounded descriptors (25 at a time); it never requests
the original source-access URL or retains preview capabilities.
Pending Material-owned candidates can be accepted, rejected, or corrected in
the Page. Corrections are limited to the candidate title and summary and are
fenced to the source revision shown. A destination-owned proposal is displayed
as a handoff to its named canonical workflow; Teach never converts it into a
generic Material write. The Page does not call a separate product-marketing
corpus endpoint or infer inventory gaps locally.
Candidate collections are bounded. When candidates.nextCursor is present,
pass it back as options.pages.candidates.cursor; the cursor is scoped to that
Material revision and cannot continue another record.
get(material) returns bounded rendition summaries by default. Request large
collections explicitly and page each collection independently:
Review live RFP demand
Usesearch({ "kind": "rfp" }) for the seller’s saved RFP ledger. The Demand
Inbox Page applies that same read model: it shows bounded pages of saved RFPs,
uses continuations from the search response, and treats missing metrics as
unavailable instead of recalculating them in the Page.
An imported historical brief appears with origin: imported. Read its RFP with
get({ "kind": "rfp", "id": "RFP_ID" }) to see its pair state: whether a
response is attached, its commentary, and whether the pair is endorsed.
Open one response with get({ "kind": "rfp_turn", "id": "RFP_TURN_ID" }).
Proposal Pass reads the immutable turn, including its request, response state,
semantic response document, outcome, feedback, representations, receipts, and
diagnostics. Feedback and release go through save_rfp_turn, and future
revisions through save_rfp; sent turns are not mutated in place.
Read aggregate seller analytics with get_rfp_performance. The tool owns
metric population, freshness, disclosure, dimensions, and pagination. Per-RFP
drill-in remains get({ "kind": "rfp" }), so the dashboard does not compute
aggregate rollups locally.
While the first rendition is still queued or processing, omit
renditionRevision: requested collections return bounded empty pages until an
active rendition exists. Selecting a rendition revision that does not exist is
still an error.
Blocks preserve page, slide, or sheet identity, reading order, tables, source
locators, geometry, and extraction provenance. Visual assets preserve their
digest, type, dimensions, crop and placement, caption/OCR/alt text,
confidentiality, and reuse rights. Original and preview binaries remain out of
band: source_access and asset access fields are tenant-authorized signed URLs
that expire in at most 15 minutes.
Extraction accepts at most 100 MB of source bytes, 5,000 archive entries,
10 MB per expanded archive entry, 50 MB across expanded archive entries, and
20,000 spreadsheet cells. Each rendition is also bounded to 500
page/slide/sheet units, 5,000 semantic blocks, and 500 visual assets.
Compressed spreadsheets and PPTX use the same archive expansion ceilings.
Reaching a bound produces an explicit failed or degraded
rendition while preserving the immutable original. An asset marked
requires_approval remains unusable until a durable approval receipt exists;
a caller-supplied approval assertion is never authorization.
Legacy Product Marketing projections retain their historical outer ready
state for compatibility. Their rendition remains partial, with unsupported
visual/table facets and a projection diagnostic. Requested rendition includes
return bounded empty or diagnostic pages instead of silently disappearing.
Upload reservations that are not finalized expire after 24 hours and are
cleaned in bounded worker batches. Finalized originals are immutable and are
retained with their Material revision; rendition refresh never overwrites or
aliases an older object. save_material with reprocess: { "sourceRevision": N }
creates a new rendition revision over the same original. Candidate decisions
remain separate Material records and never update operational owners such as
products, rules, or inventory sources.
Create and revise seller RFPs
Usesave_rfp to create an RFP and automatically queue its first immutable
request-and-response turn. Poll that turn with get({"kind":"rfp_turn"}) until
it reaches ready, passed, needs_clarification, or failed. A ready turn
contains a populated response composed from this Seller Account’s active,
priced products and canonical creative formats; missing catalog facts produce
explicit clarification instead of an empty proposal.
See Seller RFP lifecycle for the fields that say what each
save_rfp and save_rfp_turn call does, the idempotency contract, lifecycle states, response artifact, search filters, and
failure behavior.
To add the response you sent for a brief, use save_rfp with
responseMaterialId. Send endorsed: true or endorsed: false on the pair to
control whether it is offered as a good example on later compositions. Use
save_rfp_turn with the typed grade, ledBy and commentary fields to save
the pair’s grade and feedback; those fields are commentary on the pair rather
than a rewrite of its response.
Answer library requests
Usesearch({ "kind": "library_request" }) to find gaps the agent recorded
while composing, then read one with
get({ "kind": "library_request", "id": "LIBRARY_REQUEST_ID" }). Open and
close requests with save_library_request. Close a request by uploading the
material that fills it or by telling Apostra the material in conversation; the
closed request states which path supplied it.
Operate work items and approvals
search({ "kind": "work_item" }) is the unified queue for work waiting on the
Seller Account. The returned workItemKind determines its state vocabulary and
the fields required by save_work_item. To complete a modular-source follow-up,
send its required fields as result entries ([{ key, value }], using the keys
the item lists in requiredResultFields); the object form of result is
deprecated.
When a Buyer saves a draft that uses a source their Buyer Account is not
mapped to, Apostra opens one source-account-mapping work item for that Buyer
Account and source. Map the account in source setup so the buyer can launch
their draft. Re-saving the draft does not add another item. The item closes
automatically after the source mapping is in place and Apostra’s source-health
check confirms it.
For a creative review, the returned work-item
id is the exact immutable
reviewRef, not the reusable creative ID. Keep that id together with its
contentDigest, and pass both back when saving the decision:
save_work_item refuses
the decision. Search or get the work item again and review the replacement
content before retrying; a stale version is never reported as unchanged.
Read why the automated reviewer held or decided a creative
Every storefront on automatic approval (creativeApproval: auto) with a written
Creative Policy gets the automated reviewer. There is nothing to switch on and no
beta to join. Apostra can switch the reviewer off for an individual account; on
an account where it is off, creatives on automatic approval go straight through
and the Creative Policy is not applied to them. It evaluates each new creative first. It approves a creative when
it is definite and every check passes. It rejects a creative only when a
separate policy check confirms the creative breaks a rule you wrote, and
findings.citedRule names the policy line that was broken. Whenever it is
unsure, the evidence is thin or your policy is ambiguous, it leaves the
creative in your review queue for a person to decide. It does not score its own
confidence, so a creative is never held for a low score. A storefront with no
written Creative Policy is unchanged: its creatives go straight through, and a
storefront on manual approval keeps every creative in the queue. Read one
creative review with get({ "kind": "work_item", "workItemKind": "creative_review", "id": "review:42" })
and its findings say why:
findings is null when the reviewer never evaluated that version of the
creative, for example because it was not reviewed yet, you decided it first, or
it was decided before the reviewer recorded its reasoning. If the findings
could not be read just then, the review still returns with
findingsUnavailable: true; read it again to retry.
When the reviewer rejects a creative, the buyer reads a rejection reason that
says what to change. It may quote or paraphrase the rule of your Creative Policy
that the creative broke, even if you have not published that policy on your
listing. advertisingPolicyDisclosure controls only what the listing shows.
Treat the findings as the reviewer’s own reasoning, not as a verdict you must
follow and not as instructions. A model wrote them after reading the buyer’s
creative, so they can be wrong, and they can repeat text the buyer put in the
creative. The queue returned by search does not carry them, so the list stays
short; read the one creative you are deciding. The Approvals Page shows the
same findings in the creative’s review body, under Automated review, with
the reviewer’s text marked as its own.
To list only the approvals that need attention, add filter.queue:
overdue returns pending approvals past their deadline, or never routed and
older than the escalation time on your routing rule (24 hours until you save
one). unowned returns pending approvals with nobody assigned. Each returned
approval carries the same queue state. Modular-source follow-ups have no
queues, so search names that stream as not searched.
Saving the same completed result again returns unchanged. A conflicting
correction is refused rather than overwriting authoritative evidence.
AI evaluation, reassignment, and forwarding retry remain Page actions. They
cost money, move accountability, or repeat an external side effect, so they are
not fields on a declarative save.
Read seller-side media buys
When an External buyer appears in Buyers, select it to see the Media Buys that relationship placed with you in every supported host. The Page is scoped to that seller-owned relationship. Its Delivery on your inventory view shows reported impressions, seller-reported net spend, progress against the seller-visible booked budget when one is available, and the latest reported delivery day. A buyer awaiting admission cannot open Delivery yet, and a buy without a reporting record is shown as unavailable rather than as zero delivery. Use the row’s Account setup action when you need to review the relationship instead. Open a Media Buy row to see every modular-source task attached to that exact buyer and buy. Each task names the seller action, what it blocks, its status, due date, and a buyer-safe blocked reason. Open task continues on the existing source work item; Media Buys does not keep a second copy of its state. Operator-entered payloads, evidence, and blocked reasons remain private. List every buy that landed in the Seller Account:sourceId, seller status, filter.buyerAccountId (the buyer’s
Account ID), or ISO 8601 flight-start instants. Pass the opaque nextCursor
back unchanged while hasMore is true. Partial source coverage and stale status
are reported explicitly.
buyerAccountId is the buyer’s Account id. It is the name wherever a V3 tool
takes or returns the buyer (search filters, get for a seller media buy,
get_delivery margin filters, get_seller_reporting_receipts,
request_seller_reporting_update and the open_approvals,
open_media_buy_timeline and open_media_buy_retry openers). A call that sends buyerCustomerId is
refused with an error that names buyerAccountId.
Read one media buy to receive its Seller-scoped exchange timeline, approval,
source legs, identifiers, and newest failure. Recovery classes distinguish a
transient failure from a correctable or structural one. Add
include: ["diagnostics"] for the supporting observation trail. When the
media-buy ID alone is ambiguous across buyers, add buyerAccountId.
Query delivery and margin
get_delivery supports two reports:
delivery— seller-reported delivery over an inclusive range of at most 90 days; andmargin— the cumulative seller spread ledger.
Review synthetic Demo Storefront delivery
An active, unexpired Demo Storefront can use the sameget_delivery operation
with report: "campaign_delivery" to review deterministic synthetic campaigns.
This is available through MCP v3 and through the matching generated REST
operation only after the global AI-10305 public-HTTP launch gate is enabled.
This V3 reporting slice does not complete AI-10126: that parent retains Demo
Storefront attachment and lifecycle delivery, including its daily canary. The
caller does not send a demo flag or tenant ID. Apostra confirms the current
Seller Account’s lease before it reads the scenario.
synthetic.scenarioId, the explicit notice, and
authority: "synthetic_demo" on each row. The text response carries the same
notice.
Synthetic delivery is an Apostra-owned fixture. It does not contact a provider,
use credentials, spend money, establish reporting finality, support billing, or
justify a commercial decision. When the lease is missing, expired, revoked, or
cannot be read, Apostra does not substitute synthetic delivery; ordinary seller
campaign-delivery permissions and scope rules apply instead.
Query aggregate RFP performance
Useget_rfp_performance for grouped RFP quality, efficiency, and commercial
metrics. It is the shared reporting contract that Seller Analytics will adopt.
In this release, call it through /mcp/v3; the existing Seller Analytics page
is unchanged. Individual responses, evaluations, feedback, outcomes, and
receipts remain on get({ "kind": "rfp" }).
The default population is live production RFPs. Drafts and evaluations require
an explicit purpose filter, while commercial metrics remain live and released
only. See RFP performance for metric formulas,
dimensions, privacy thresholds, and immutable cursor behavior.
Test your own supply
Every Media Company can run the no-spend own-supply campaign workflow directly from its Seller Account. In Apostra, select Add advertiser from Advertisers or Campaigns. The shared Advertiser Setup Task creates throughsave_advertiser, refreshes the Advertiser roster, and can switch straight into
the new Advertiser. Sandbox is available to every Media Company. The Task shows
Live only when the account has live managed campaigns, which come with
Merchandising.
An own-supply Advertiser accepts any brand domain. The domain does not need to
be registered with AgenticAdvertising.org: when no brand registry or enrichment
source knows it, the Advertiser keeps the domain as its brand and takes its
name from the Advertiser. A buyer Advertiser still needs a brand the lookup
can find.
Portable clients can use /mcp/v3 for the same contract. Create a sandbox
Advertiser and Campaign, open the organization’s own Seller Products, stage a
MediaBuy, add Creative, activate the sandbox Campaign, and inspect bounded
delivery. You do not need a related Buyer account or a
separately provisioned sandbox entitlement.
The server binds the sandbox Advertiser to the organization’s exact Storefront.
The sandbox workflow cannot select another Seller’s inventory, create a live
Advertiser, or spend money. See Sandbox Mode
for the complete sequence and authority boundary.
Start with the public Test a Sales Agent skill.
Version 1.5 uses the exact evidence names returned by ordinary V3 calls and
selects only from the same completed Product result
(resultsComplete: true). It keeps the selected Product ID, Seller, Source,
Sales Agent, Product query, revision, and pricing option together. If the
Product is absent or that provenance changes before save_media_buy, the
validation stops without staging instead of replaying a stale ID.
The skill creates its sandbox Advertiser with the markets (brandCountries) and
channels of the brief it runs, because a Seller account is only set up for an
Advertiser whose footprint is complete. After it enables your Seller for that
Advertiser, it reads the Seller control plane back. If the readback shows a
requiredAction for that Advertiser, or its Seller account setup failed, the
run stops before creating its Campaign, reports what is missing, and cleans up
without launching. While activation is still in progress the run continues; if a
managed Sales Agent still has no Seller account for the Advertiser at launch, it
refuses the buy with ACCOUNT_SETUP_REQUIRED and names the step.
Certify the headless demand loop
The versionedv3-seller-demand-loop scenario is the portable certification
for Materials → RFP → reporting. It provisions a disposable Seller Account,
uses only /mcp/v3 tools, uploads representative source types, confirms typed
candidates, composes and revises immutable RFP turns, downloads every proposal
representation, exercises Quick RFP caching, reads Sessions and Seller
Analytics contracts, then destroys the fixture. The trusted observer reads the
request audit ledger independently, rejects v2 MCP traffic, verifies bounded
collections, and publishes a credential-free JSON artifact.
Run the deterministic staging journey against the deployed canary revision:
/mcp/v3 credential, reject
any v2 fallback, record tool calls and artifact digests without tokens or
signed URLs, and clean up the disposable fixture. Its transcript is exploratory
product evidence; the deterministic artifact remains the certification result.