> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# v3 Tool Catalog

> The stable tool families exposed by the account-resolved v3 MCP endpoint.

<Note>
  `tools/list` is authoritative for the active account. A principal with both
  Buyer and Seller access receives their deduplicated union at connection time,
  while calls remain authorized against the selected account. Enrollment,
  permissions, feature rollout, and host support can narrow the catalog. This
  page explains the complete registered families and intended use.
</Note>

## Navigation and shared tools

The [Identity and brands guide](/v2/setup/v3/identity-setup) covers current
identity and provenance, domain correction, public brand lookup, confirmation
and human proof follow-up in text-only clients. These operations use
`get_status`, `get`, `save_buyer_operator`, `save_advertiser` and `save_seller`.
Select `identityContract: "confirmed-v1"` for the preview and confirmation
workflow; existing save payloads keep their earlier contract.

| Tool                         | Accounts                              | Purpose                                                                                                                                                                                                                                    |
| ---------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `get_status`                 | Every account                         | Read active account, readiness, blockers, next actions, and reachable accounts                                                                                                                                                             |
| `switch_account`             | Every account                         | Move to a reachable account or return home                                                                                                                                                                                                 |
| `save_ask`                   | Every account                         | File or update support, product, supply, integration, or commercial asks                                                                                                                                                                   |
| `save_notification_config`   | Shared, when enabled                  | Save typed notification thresholds and preferences                                                                                                                                                                                         |
| `search`                     | Buyer, Seller, and Organisation       | Search supported kinds, documentation, or the AdCP specification; Organisation accounts receive documentation search only                                                                                                                  |
| `get`                        | Buyer and Seller                      | Read an object or singleton plus supported includes                                                                                                                                                                                        |
| `open_page`                  | Buyer and Seller                      | Open an account-appropriate MCP App Page                                                                                                                                                                                                   |
| `test_creative_macros`       | Buyer and Seller                      | Dry-run one tracker URL from raw vendor syntax through AdCP, recipient syntax, and synthetic values                                                                                                                                        |
| `open_connections_page`      | Buyer                                 | Open Media Partners, optionally seeded by a known positive integer-string `advertiserId` or `sellerId`; `connectionAction: "connect"` opens setup and requires `sellerId`                                                                  |
| `open_creative_engines_page` | Buyer                                 | Open Creative Engines, optionally focused on a registered positive integer-string `engineId` or an existing `connectionId`; `connectionAction: "connect"` requires `engineId` and focuses secure setup, which the buyer starts in the Page |
| `open_advertisers_page`      | Buyer                                 | Open the Advertisers Page — every advertiser with campaign and draft counts and one next action each; see [Open Advertisers](/v2/buyer/advertisers/tasks/open-advertisers-page)                                                            |
| `open_campaigns_page`        | Buyer                                 | Open the Campaigns Page, optionally scoped by a positive integer-string `advertiserId` or focused by `campaignId`; see [Open Campaigns](/v2/buyer/campaigns/tasks/open-campaigns-page)                                                     |
| `open_campaign_receipt`      | Buyer                                 | Open Review & go live for one draft `campaignId` — plan, staged media buys, and readiness blockers; refuses non-draft campaigns; see [Open Review & go live](/v2/buyer/campaigns/tasks/open-campaign-receipt)                              |
| `open_approvals`             | Seller                                | Open the seller's governed approvals Page                                                                                                                                                                                                  |
| `open_media_buys_page`       | Seller                                | Open Media Buys, optionally scoped to one seller-owned account relationship                                                                                                                                                                |
| `open_proposal_pass`         | Seller                                | Open the portable Proposal Pass Page for one exact immutable `rfpId`/`turnId` pair; see [RFP lifecycle](/v2/setup/v3/rfp-lifecycle#proposal-pass-the-portable-turn-workbench)                                                              |
| `save_session`               | Platform operator in a seller account | Persist an idempotent private coaching note on one retained native Murph Session; it does not send a customer message                                                                                                                      |

### Catalogs

Buyer accounts can search and read their advertiser-scoped catalogs with
`search(kind: "catalog")` and
`get(kind: "catalog", id, advertiserId: "12345")`. Use
`save_catalog` to declare one AdCP Catalog or archive it. Item reads are a
bounded `get` include and never expose feed credentials.

### Open Media Buys

`open_media_buys_page` opens the Seller Media Buys Page. It accepts an optional
positive-integer-string `accountRelationshipId` and optional `view` of
`media_buys`, `creatives`, or `delivery`. With no relationship ID, it opens the Seller's
unfiltered Media Buys Page.

When a relationship ID is supplied, the tool first proves that the relationship
belongs to the authenticated Seller Account. An unknown or another seller's
relationship fails closed: it returns no Page directive and opens nothing. The
Page then shows only that buyer relationship's activity with the Seller — its
media buys and synced creatives — never the buyer's own campaigns.

Every `search` call must provide `query`, `kind`, or both. An empty call is
invalid. Use `query` for a match term or documentation question and `kind` to
list account objects.

A Buyer can browse the selected advertiser's visible products across eligible
Sellers before creating a campaign:

```json theme={null}
{
  "kind": "wholesale_product",
  "sources": ["objects"],
  "filter": { "advertiserId": "ADVERTISER_ID" }
}
```

Each result includes the exact opaque `productId`, `sellerId`, and Seller name
needed for a subsequent `creative_format` search. Pagination traverses each
qualified product once, even when multiple seller proposals reference it. Pass
`nextCursor` back with the same advertiser and `limit`. When `resultsComplete`
is false, replace the provisional page after polling; when `partial` is true,
do not treat an absent product as unavailable. `persistenceIncomplete`
identifies a terminal partial result where otherwise visible products were
withheld because a complete, buyer-safe identity could not be persisted;
restart without a cursor to retry them. This read may use live or cached
catalogue results and does not create a proposal, campaign, or media buy.

A Creative search must name exactly one owner. Use `filter.advertiserId` to
browse the advertiser's visible Creative summaries, or `filter.campaignId` to
list summaries attached to one campaign:

```json theme={null}
{
  "kind": "creative",
  "filter": {
    "advertiserId": "ADVERTISER_ID",
    "promoted": true
  }
}
```

Advertiser scope supports `formatKind`, `assetType`, `role`, `source`, and
`promoted`. Omitting `promoted` returns all visible Creatives; `true` selects
the promoted reusable shelf, and `false` selects non-promoted Creatives.
Campaign scope supports the top-level `query` only. Each successful response
repeats the effective owner scope and filters in both text and structured
output. Results are plain Creative summary rows; this search does not open or
return a Creative Library Page. Pass `nextCursor` back only with the same
selected account, owner scope, effective filters, and limit.

A Seller asking what it currently offers uses:

```json theme={null}
{ "kind": "wholesale_product" }
```

### Conversation history

Buyer and Seller members can explicitly search their prior Murph rooms with
`kind: "conversation"`:

```json theme={null}
{
  "kind": "conversation",
  "query": "campaign pacing",
  "filter": {
    "conversationStartedAfter": "2026-09-07T00:00:00Z",
    "conversationStartedBefore": "2026-09-08T00:00:00Z"
  }
}
```

The result includes only the member's own rooms and explicitly shared rooms in
the active account. Matches include bounded, display-safe `matchedText`
evidence from the matching title, participant, or message, even when a matching
message is older than the recent transcript tail. Follow a returned id with
`get({"kind":"conversation","id":"..."})` to read up to 20 recent,
display-safe user/assistant messages. That bounded detail read does not limit
the history searched. Tool output and system context are never returned.
Service credentials without an end-user identity fail closed, and conversation
search runs only when explicitly requested; it is not included in an
unqualified cross-kind search.

### Retained Sessions

Retained Sessions are restricted to signed-in platform operators acting in a
seller account without user impersonation; they are not enabled for customer
use. `search({"kind":"session"})` finds only native Murph conversations whose
history has already been retained.
`get({"kind":"session","id":"..."})` reads that retained history, and
`save_session` can add an idempotent private coaching note. See the
[Retained Session timeline](/v2/setup/v3/session-timeline) for access,
cursor, import-limit, and unsupported-operation boundaries.

### `save_session`: record a private note

`save_session` accepts only `action: "record_note"`. Use the exact
`sessionId` returned by Session search or read, a `clientRequestId`, and the
private `note`. `throughEventId` is optional; when supplied, it anchors the
note to one accessible retained event in that Session.

`sessionId`, `clientRequestId`, and `throughEventId` are opaque strings. They
are trimmed, must not be empty, and can contain at most 512 characters. The
private `note` is also trimmed and required, with a maximum of 8,000
characters.

```json theme={null}
{
  "action": "record_note",
  "sessionId": "SESSION_ID",
  "clientRequestId": "note-001",
  "throughEventId": "EVENT_ID",
  "note": "Explain the price before asking for a commitment."
}
```

The MCP `structuredContent` response is limited to the write result:

```json theme={null}
{
  "action": "record_note",
  "sessionId": "SESSION_ID",
  "receipt": null
}
```

It does not return the full Session or event, and it does not say whether a
note was created or replayed. Re-read the Session with
`get({"kind":"session","id":"SESSION_ID"})` to inspect the retained
note.

The same scoped request key with the same content replays the saved note.
Reusing that key with changed content returns a conflict. The tool never sends
an outbound message. A retry is denied when current access no longer permits
the note, and every action other than `record_note` is unsupported.

This tool is listed only for a signed-in platform operator in an active seller
account. It requires no user impersonation and the current native-room
permission check. The seller catalog entry does not expose it to customers;
the tool also rechecks the operator, end-user, and seller boundaries at call
time. See the [Retained Session timeline](/v2/setup/v3/session-timeline) for
the complete access and retry rules.

### Seller and product filters

For `search({"kind":"wholesale_product"})`, add `sourceId` to narrow
the list to one ad-server-backed source, or
`filter.productStatus` to select `draft`, `active`, or `archived` products.
For Buyer seller reads, `filter.ids` accepts up to 50 exact Storefront ids in
one call, preserves requested order, and reports unavailable ids as
`objects.missingIds`.

As an alternative Buyer read scoped to one Seller, call `get` with
`kind: "seller"`, that exact Seller `id`, the current account's
`advertiserId`, and `include: ["products"]`. Preserve the returned
`productQueryId` and catalog revision when polling the same request to a
terminal result; do not reuse a Product from an older catalog.

Buyer `search(kind:"seller")` also accepts these filter fields to narrow results:

| Filter field              | Type      | Values / notes                                                                                                                  |
| ------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `filter.classification`   | string    | `global_market_maker`, `regional_market_maker`, or `marketplace_seller`                                                         |
| `filter.marketplaceReady` | boolean   | `true` returns only marketplace-listed, transaction-ready sellers                                                               |
| `filter.channel`          | string\[] | Any channel the seller carries (e.g. `display`, `ctv`). Results include sellers that carry at least one of the listed channels. |
| `filter.region`           | string    | Region code the seller covers (e.g. `EMEA`, `NORAM`, `APAC`)                                                                    |

`seller` and `connection` are intentionally separate buyer nouns. A seller is
one exact Storefront commercial counterparty and can exist before authorization.
A connection is one authorization grant, identified by its typed `target`.
Seller grants retain `sellerId`. Enrolled buyers can also connect a
[Creative Engine](/v2/setup/v3/creative-engines) using
`target: {kind: "creative_engine", id: "..."}` and search its grants with
`filter.targetKind: "creative_engine"`. Omit that filter to list seller grants.
One seller can have zero, one, or multiple connections, and each connection can
contain multiple discovered provider accounts. Use `search(kind:"connection")`
for questions such as “who am I connected to?” and join the returned `sellerId`
to the seller catalog when names or marketplace policy are needed.

A `get(kind:"seller")` call for a single seller also returns
`connectionState.directedCampaignSubscription` when an active directed campaign
subscription exists between the buyer and that seller. The field is absent on
search results and null when no subscription exists.

| Field                                                         | Type           | Notes                                            |
| ------------------------------------------------------------- | -------------- | ------------------------------------------------ |
| `connectionState.directedCampaignSubscription.status`         | string         | `ACTIVE`, `PAUSED`, or `ERROR`                   |
| `connectionState.directedCampaignSubscription.lastSyncedAt`   | string \| null | ISO timestamp of the most recent sync            |
| `connectionState.directedCampaignSubscription.lastSyncStatus` | string \| null | `SUCCESS` or `ERROR`; null before the first sync |

## Internal Marketplace administration

Directly authenticated Apostra SuperAdmins may also receive two internal tools:

| Tool                               | Purpose                                                                                                              |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `preview_marketplace_supply_batch` | Read up to 25 exact Storefronts in one batch and return current revisions, blockers, and proposed Market Maker scope |
| `apply_marketplace_supply_batch`   | Apply the confirmed, revision-guarded governed reviews and return one outcome per Storefront                         |

These tools are omitted from every non-SuperAdmin catalog and re-check staff
authorization at execution. Apply performs a whole-batch stale-state preflight,
then commits each Storefront independently. Its response labels this behavior
`BEST_EFFORT_WITH_BATCH_PREFLIGHT`; callers must inspect every item before
retrying failures.

## Plan and billing

| Tool                            | Accounts         | Purpose                                                       |
| ------------------------------- | ---------------- | ------------------------------------------------------------- |
| `save_billing`                  | Every account    | Accept Terms or create and poll a secure card-capture handoff |
| `get_plan_billing_account`      | Buyer and Seller | Read plan, agreement, and billing readiness                   |
| `accept_plan_billing_tos`       | Buyer and Seller | Record authorized Terms acceptance                            |
| `set_plan_billing_payout_payee` | Buyer and Seller | Launch the governed payout-payee flow                         |

`save_billing` is the model-visible shared settings tool. It accepts exactly one
intent per call: `terms` or `paymentAuthority`. Terms acceptance requires the
current version shown in Plan & Billing or the required-terms response, plus
direct organization-admin authority:

```json theme={null}
{
  "terms": {
    "accepted": true,
    "version": "v3-0"
  }
}
```

If the account is already governed by a custom contract, no standard Terms
acceptance is recorded. The tool returns `action: "terms_not_required"` with
`terms.accepted: false` and `terms.governedByExistingContract: true` so the
agent does not report a legal acceptance that did not occur.

Card setup keeps all card data outside MCP. Call `paymentAuthority` with
`action: "request"`, then repeat with `action: "confirm"` and the returned
`confirmationToken`. The confirm response has
`action: "human_action_required"` and a one-time
`paymentAuthority.url` for the organization's cardholder. Poll with
`action: "status"` every 15–30 seconds until the nested status is `verified`
or `expired`. The only supported method today is `capture_link`; branch on the
returned `method` so future methods remain additive.

Legal acceptance and payout changes remain authorization-sensitive. The three
other tools in this table are private Plan & Billing Page capabilities and may
be omitted from model context. Follow the tool response and Page requirements;
never pass card or bank details through free text.

## Buyer tools

| Tool                       | Purpose                                                                                                                                                                                                                                                                                          |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `save_advertiser`          | Create or patch an advertiser, including inherited tracking defaults; archive or restore one advertiser (`advertiserId`) or several in one call (`advertiserIds`)                                                                                                                                |
| `save_buyer_operator`      | Confirm the buyer account's commercial operator identity used with sellers; this does not grant login access                                                                                                                                                                                     |
| `save_campaign`            | Create or patch a campaign, including local trackers and inherited-tracker overrides; set the full set of creatives on it with `creativeIds`; pause, archive, restore, reactivate, explicitly launch, or cancel it when its media buys are already settled                                       |
| `save_dimension`           | Create or update a buyer-owned dimension and its values; labels remain fields on advertisers and campaigns                                                                                                                                                                                       |
| `upload_creative_asset`    | Open the private upload Task for one buyer-owned advertiser; no destination connection or provider account is required                                                                                                                                                                           |
| `save_creative`            | Create, update, or archive a creative; use `sourceAssetRef` for one finalized image or `sourceAssets` with the selected `creativeFormatId` to bind finalized images and promoted MP4s to canonical format slots; set the full set of campaigns an advertiser creative runs on with `campaignIds` |
| `save_creative_collection` | Create or change a non-executable advertiser or campaign collection; advertiser writes use a revision and saved-creative members only                                                                                                                                                            |
| `request_proposals`        | Ask every seller currently eligible for the buyer for proposals or products against one campaign revision                                                                                                                                                                                        |
| `save_media_buy`           | Accept a proposal, stage returned products, update a draft media buy (flight, budget, bid, pricing option, line items by `lineItemRef`), or archive an unwanted draft                                                                                                                            |
| `get_delivery`             | Query bounded Buyer campaign delivery                                                                                                                                                                                                                                                            |

Buyer `search` and `get` kinds include the account's advertisers, campaigns,
creatives, creative formats, creative collections, wholesale products,
proposals, media buys, sellers, asks, and organization-owned Agents, plus other
kinds advertised by the live schema. Follow the [Buyer
workflows](/v2/setup/v3/buyer-workflows) page for sequencing.

On every buyer read, a `sellerId` identifies a Storefront. It is not an internal
customer ID. For one compatibility release, a seller's deprecated
`connection.customerId` remains available for existing integrations. Use
`sellerId` or `storefrontId` instead; `connection.customerId` will be removed
in a follow-up release after this transition period.

For a saved Creative, add `include: ["preview"]` to `get`. The read returns a
15-minute render URL bound to the exact current Creative state revision, or a reason
when the format has no first-party renderable asset. The URL runs only on the
isolated Apostra render origin. A changed Creative invalidates the old URL,
and the response never includes storage URLs, provider preview HTML, or
measurement trackers. Direct image, video, and audio source assets are
renderable; active HTML, VAST, DAAST, and ZIP documents return a reason instead.

Before assembling uploaded assets, search the selected advertiser's visible
canonical format declarations. This read does not require a campaign and does
not make a provider call or setup request. Seller-owned product declarations
are available without a provider connection; direct-adapter products keep
their existing active-connection visibility rules:

```json theme={null}
{
  "kind": "creative_format",
  "filter": {
    "advertiserId": "ADVERTISER_ID",
    "productId": "PRODUCT_ID"
  }
}
```

`productId` is required to keep the catalogue read bounded. Add `sellerId` or
`formatKind` when the selected product supplies that scope.

Each result returns an opaque `id`, the seller's display name and id, the exact
`formatOptionRef`, its canonical `formatKind`, any declared frame and size, and
its slots. Every slot includes its stable `slotId`, required flag, minimum and
maximum cardinality, accepted asset kind, and the exact authored constraint
fields supplied by the product. Use the opaque result `id` with
`get({kind: "creative_format", id, advertiserId})` for stable readback. The
read includes only currently buyer-visible product and publisher declarations;
it does not add entries from the legacy global format catalogue. Pass the
returned `id` as `creativeFormatId` when calling advertiser-scoped
`save_creative`. The server revalidates the selected advertiser, seller,
product route, option, and declaration before reading an asset or writing a
Creative. If `formatKind` or `formatOptionRef` is also supplied, it must match
the selected format.

Search creative collections with exactly one campaign or advertiser scope. You can
create an advertiser collection without attaching it to a campaign. To read one,
use `get` with `kind: 'creative_collection'`, its id, and `advertiserId`. To change
an advertiser collection, use `save_creative_collection` with `advertiserId`,
`collectionId`, and the returned `updatedAt` as `expectedUpdatedAt`. Updates allow
name, description (`description: null` clears it), or `parentId` (`parentId:
null` clears it); member changes accept saved creative ids only. Results include
`parentId` and `descendantMemberCount`, which counts distinct members across the
collection and its active descendants. A parent must belong to the same
advertiser, cannot be the collection or one of its descendants, and cannot make
the hierarchy deeper than 16 levels. Archive with `isArchived: true` only after
archiving child collections. Restore with `isArchived: false` and the archive
response's `expectedUpdatedAt`; a collection with a parent can be restored only
while that parent is active and belongs to the same advertiser. A stale revision
does not change the collection, and `expectedUpdatedAt` is refused on campaign
collections, which carry no revision fence. Advertiser collections remain
non-executable: they do not attach campaigns or set roles, sync policy, or
arbitrary metadata.

### Replay an uncertain advertiser create

When creating an advertiser, you may supply an `idempotencyKey` of 16–255
letters, numbers, underscores, periods, colons, or hyphens. Use one key for one
logical create and reuse that exact key with the unchanged create payload if the
response is lost or uncertain. The replay returns the originally created
advertiser; changing the payload with the same key returns a conflict. The key
is create-only and optional, so a create without it retains ordinary create
behavior. Keep `correlationId` unique for each transport attempt: it is a
diagnostic handle, not the replay key.

### Create and verify a campaign creative

Call `save_creative` with a `campaignId`, `name`, and optional `assets` to add a
creative directly to a campaign. Each asset supplies exactly one public `url`
or base64 `dataUrl`, plus its `assetType`. The direct tool accepts image, video,
audio, HTML, VAST XML, and text media; ZIP bundles continue through the inspected
[HTML5 upload flow](/v2/object-guides/creative#html5-bundles). Add `clickUrl`
when the creative has a click-through destination. `makePrimary` is available
for image, video, audio, HTML, and VAST assets; text can be attached only as a
subsidiary asset.

For a local JPEG, PNG, or MP4, select the owning `advertiserId` and call
`upload_creative_asset`. A destination Storefront connection and provider
account are deliberately not part of this V3 source identity.
The Task hashes the file in the browser and sends the bytes directly to private
storage. File bytes never enter model or chat context. The signed PUT URL is
returned only to the embedded Task app; it is excluded from model-visible
results, chat/activity, logs, and persistence.

Configured buyer ingress accepts JPEG and PNG files of 1–30,000,000 bytes.
When managed-video ingress and inspection are configured, authenticated Buyer
Accounts also accept MP4 files of 1–2,000,000,000 bytes.
Finalization verifies the declared MIME type, exact size, and digest. MP4
finalization also reports asynchronous media inspection state. An expired
preparation must be started again; retrying a completed upload returns the same
source identity.

Pass a finalized JPEG/PNG source reference verbatim as `sourceAssetRef` to
`save_creative`. The advertiser is the supplied `advertiserId`, or is derived
from `campaignId`.

For an eligible reference, use `sourceAssetRef` instead of `assets`:

```json theme={null}
{
  "campaignId": "CAMPAIGN_ID",
  "name": "Autumn hero",
  "sourceAssetRef": "scope3-asset://v1/123e4567-e89b-42d3-a456-426614174000",
  "clickUrl": "https://example.com/autumn"
}
```

The server accepts only the customer, authenticated principal, and advertiser
that owned the finalized upload. It re-verifies the private bytes, copies them
into the governed creative asset store, and saves only the durable asset URL
plus a one-way source fingerprint. Private object paths, source references,
and signed URLs are never persisted on the creative.

With `campaignId`, the creative is created under that campaign's advertiser
first and then attached through normal campaign membership. Retry the same
`sourceAssetRef` and `name` after an uncertain response: an existing adoption
is returned and any interrupted campaign attachment is completed. A different
name is refused rather than renaming or duplicating the adoption. Use
`advertiserId` instead of `campaignId` to keep the creative in the advertiser
library for later assignment.

To assemble uploaded assets before a campaign exists, search the selected
product with `search({kind: "creative_format", filter: {advertiserId,
productId}})`, then pass the result's opaque `id` as `creativeFormatId`. Bind
each durable asset to a declared slot through `sourceAssets[].assetId`. The
upload Task does not choose a format. `save_creative` derives the canonical
`formatKind` and `formatOptionRef` from the signed selection and resolves only
its exact seller and product route.

Use `advertiserId` with `creativeFormatId` to keep the assembled Creative in
the Library without a campaign. For a campaign-scoped create, use `campaignId`,
`formatKind`, and the campaign product's exact `formatOptionRef` instead; the
save also attaches the new Creative to that campaign. V3 does not yet attach an
existing advertiser-only Creative; use the V2
`attach_creatives_to_campaign` operation for that later step. Ordinary
`assets` are unbound and
cannot satisfy those slots in the same call. Repeatable groups use
`group[index].member`: bind uploaded media through `sourceAssets[].slot` and
text or URL members through `social.components[].slot`, using the same
zero-based index for every required member in an instance. Indices must be
contiguous from zero. For example, a hosted-video format can bind an MP4 to
`video` and a JPEG or PNG to `thumbnail`:

```json theme={null}
{
  "advertiserId": "ADVERTISER_ID",
  "name": "Autumn video",
  "creativeFormatId": "SIGNED_CREATIVE_FORMAT_ID",
  "sourceAssets": [
    {
      "assetId": "123e4567-e89b-42d3-a456-426614174000",
      "slot": "video",
      "makePrimary": true
    },
    {
      "assetId": "223e4567-e89b-42d3-a456-426614174000",
      "slot": "thumbnail"
    }
  ]
}
```

To save an incomplete assembly, add `"mode": "draft"`. A draft is scoped to
the advertiser and can omit required media, copy, destination, or repeat-group
slots. The response and every later Creative read return its `state` and
whole-state `stateRevision`. The existing opaque `revision` remains the
buyer-preview projection digest.

Revise the same draft by sending its `creativeId`, current `stateRevision` as
`expectedRevision`, the signed `creativeFormatId`, and the complete replacement
`sourceAssets` and authored-slot state. The save replaces the prior submitted
state; it never merges two revisions. A stale revision fails without changing
the Creative.
Use `"mode": "complete"` on that same replacement when all required slots are
present. Completion runs the format's current server-side validation and keeps
the Creative in the advertiser Library without attaching it to a campaign or
contacting a provider. Existing calls that omit `mode` keep complete-save
behavior.

The server re-proves every source against the advertiser. A video must have
completed inspection and reached `promoted`. Saving under `advertiserId`
creates the canonical Creative without a campaign or provider call. Saving
under `campaignId` also attaches it. Video delivery remains
`destination_required` until V2 `attach_creatives_to_campaign` resolves a
compatible selected Meta destination. That governed attachment rechecks the
durable Library source and current account authorization, then uses normal Meta
review and readback. Other destinations remain unsupported for this bridge. Do
not substitute the provider-bound V2 adoption operation for a buyer-owned V3
source.

After the campaign executes, call `get` with `kind: "creative"`, `sourceId` set
to the campaign ID, and `id` set to the returned creative ID. When a legacy
Sales Agent library receives the creative through `sync_creatives`,
`platformLinks` rows show the
downstream ad-platform sync status, platform ID, approval status, and last sync
time. Inline package delivery does not create a `platformLinks` row. Each agent
has one authoritative row: the most recently updated account lineage wins
after a credential or account rotation. A platform ID confirms that the
destination created its creative object; the status and approval fields still
determine whether it is ready to deliver. Existing metadata-only
`save_creative` calls require no changes.

Start with `get_status`. Its `operatorIdentity.usableForBuying` field is false
when the operator is missing or still uses a platform-owned legacy fallback.
In that state, an account administrator must call `save_buyer_operator` with
the buyer's real non-platform domain and choose `whole_operator` or a stable
`specific_unit` before new discovery or buying work. When the domain is usable,
`scopeStatus` is `unclassified`, and `locked` is false, reuse that domain and
choose its scope before new AdCP 3.2 provisioning. If the identity is locked,
follow the support action from `get_status` instead. This confirms the
commercial operator identity used with sellers; it does not add users, change
account membership, or change the login organization.

### Launch a campaign with explicit confirmation

Launching with `save_campaign` is a two-call operation. First request a
preview without changing the campaign:

```json theme={null}
{
  "campaignId": "CAMPAIGN_ID",
  "desiredPhase": "active",
  "idempotencyKey": "campaign-launch-preview-v1"
}
```

This returns `action: "pending_confirmation"` — a successful, structured
response, not an error — with `campaign.revision`, the campaign budget and
flight, and under `launch` exactly what would go live: `mediaBuys` (each
staged media buy's id, name, phase, budget, and seller; the first 25, with
`mediaBuysTruncated: true` if there are more), `mediaBuyCount`, and
`combinedBudget` (the sum across those buys when they share a currency;
`null` with a per-currency `budgetsByCurrency` list when they do not).
`nextStep` carries the exact follow-up call, including a fresh `idempotencyKey` for the confirmation (the preview's key must not be reused, or the preview is replayed). It does not launch the campaign.
Do not combine this preview with campaign field changes; save those changes in
a separate call before requesting the preview.

After reviewing the preview, send that `campaign.revision` as
`expectedRevision` with explicit confirmation:

```json theme={null}
{
  "campaignId": "CAMPAIGN_ID",
  "desiredPhase": "active",
  "confirmLaunch": true,
  "expectedRevision": 7,
  "idempotencyKey": "campaign-launch-confirm-v1"
}
```

`confirmLaunch: true` requires the `expectedRevision` returned by the preview.
The server checks that revision again while holding the campaign mutation lock,
before any provider dispatch. If the campaign changed after the preview, the
call returns `REVISION_CONFLICT`; re-read the campaign, request a new preview,
and confirm that new revision instead of reusing the earlier confirmation.

### Cancel a campaign without canceling its media buys

To cancel a campaign, send `desiredPhase: "canceled"` by itself with the
campaign's current `expectedRevision`:

```json theme={null}
{
  "campaignId": "CAMPAIGN_ID",
  "desiredPhase": "canceled",
  "expectedRevision": 7,
  "idempotencyKey": "campaign-cancel-v1"
}
```

This is a non-cascading terminal transition. It never cancels a media buy for
you. If any executable or unsettled media buys remain, the request is rejected
before changing the campaign and names every blocking media buy. Explicitly
cancel or settle those buys first, then retry with a fresh read of the campaign.
When there are no blockers, the returned campaign has `phase: "canceled"`.
If the cancellation response is lost, re-read the campaign and retry with its
committed revision. An already cancelled campaign then returns its terminal
state; this is terminal-state convergence, not a replay of the original
response body.
Archive is separate: use `isArchived: true` only when you want to remove a
campaign from default visibility.

### Archive and restore

`save_advertiser` and `save_campaign` archive with `isArchived: true` and
restore with `isArchived: false`. Archiving is a soft delete: the record moves
to archived state and everything under it is retained and stays readable.
Archiving an advertiser does not lock its campaigns, media buys, or creatives
— `search` and `get` on those children keep working — and it frees the
advertiser's name for a new advertiser. Restoring an advertiser whose name is
now held by an active advertiser returns `CONFLICT`; rename or archive the
other advertiser first.

To archive several advertisers in one confirmed action, pass `advertiserIds`
(up to 50) with `isArchived` instead of `advertiserId`. Each advertiser is
archived or restored independently and the response lists every outcome under
`results`; `partialWrite: true` marks a mixed result.

A restored campaign comes back as a draft: its media buys were retired when it
was archived, so stage inventory again before launching. `save_media_buy`
archives a **draft** media buy only (`isArchived: true`); a dispatched buy is
cancelled through the campaign or the v2 update contract, because that
cancellation may wait on seller or operator approval and must not be reported
as done before it is. An archived media buy cannot be restored; stage a new one
on the campaign.

### Advertiser and campaign tracking

Tracking is a typed field on the existing `advertiser` and `campaign` nouns;
it is not a separate V3 noun or tool family. V3 uses the same tracking services
as the V2 REST compatibility surface.

Set advertiser defaults with `save_advertiser`. This HappyDemics example keeps
the raw vendor tag, identifies its documented dialect, and lets the server
compile it to AdCP universal macros:

```json theme={null}
{
  "advertiserId": "ADVERTISER_ID",
  "tracking": {
    "enabledMacros": ["DEVICE_ID", "GDPR_CONSENT"],
    "trackers": [
      {
        "name": "HappyDemics exposure",
        "vendorName": "HappyDemics",
        "url": "https://c.ahdmw.com/pxl/EXPOSURE_ID?gdpr_consent={{GDPR_CONSENT_STRING}}&udid={{USER_ID}}",
        "trackerType": "impression",
        "sourceDialect": "happydemics"
      }
    ]
  }
}
```

Read it back with:

```json theme={null}
{
  "kind": "advertiser",
  "id": "ADVERTISER_ID",
  "include": ["tracking"]
}
```

The advertiser's `tracking.trackers[]` returns a stable `trackerId`, the
retained `rawUrl`, the translated `canonicalUrl`, and the auditable mapping
metadata. In the example, documented HappyDemics tokens canonicalize as
`{{USER_ID}}` → `{DEVICE_ID}` and `{{GDPR_CONSENT_STRING}}` →
`{GDPR_CONSENT}`. Other double-brace spellings are not inferred without their
own vendor evidence.
V3 returns bounded mapping, diagnostic, and documentation summaries with their
total counts; the V2 tracking-config response retains the complete stored
mapping artifact for non-model audit clients. Long URL values use bounded
previews with their original length and a truncation flag, and effective
campaign entries do not duplicate the source tracker's mapping artifact. When
updating an existing tracker, send its `trackerId` and omit `url` to preserve
the complete stored URL; a returned truncated preview is also recognized and
will not overwrite that stored value. Send the returned advertiser `revision`
or campaign `campaignRevision` as `tracking.expectedRevision` to make any
tracking update an atomic compare-and-set; URL-preserving updates add that
guard automatically.

Tracker URLs are limited to 8,192 characters and 64 macro tokens. For a custom
event, set `trackerType` to `custom` and supply `customEventName`; that field is
invalid for impression or click trackers. Stored free-form tracking values
returned by V3 are bounded and fenced as untrusted external data. Tracker
labels, custom event names, and custom-macro descriptions can be passed back
unchanged on an update; the server restores their exact stored values. New or
changed custom-macro descriptions are limited to 240 characters.

Before saving or trafficking a tracker, call `test_creative_macros`. The tool
does not fetch the URL or change a creative. Standard request auditing retains
only statuses and counts, never tracker URLs or binding values. The tool
returns four independently
copyable stages—exact raw input, canonical AdCP form, recipient form, and final
synthetic substitution—plus mapping evidence, bindings, and diagnostics.

```json theme={null}
{
  "rawInput": "https://c.ahdmw.com/pxl/EXPOSURE_ID?udid={{USER_ID}}",
  "sourceDialect": "happydemics",
  "targetDialect": "gam",
  "scenario": "device_id_present",
  "scenarioKey": "review-1"
}
```

For this example, the stages show `{{USER_ID}}` → `{DEVICE_ID}` →
`%%ADVERTISING_IDENTIFIER_PLAIN%%`, while the final stage uses a visibly
synthetic advertising identifier.

For consent syntax inspection, `gvlVendorId` can show the corresponding GAM
`${GDPR_CONSENT_<id>}` form, but a caller-supplied number is not verification:
the report remains failed and not preview-ready. Without the number, the
consent parameter is removed and the report also fails closed. A production
consent mapping needs separate evidence that the ID belongs to the measurement
vendor; the tester never infers that relationship from a pixel URL.

Available scenarios are `device_id_present`, `device_id_unavailable`,
`gdpr_applies_with_consent`, `gdpr_does_not_apply`, and
`missing_required_value`. The unavailable-device scenario deliberately shows
an empty device value and labels it `privacy_suppressed`. Change `scenarioKey`
to obtain a different deterministic cachebuster. Explicit `values` must be raw
synthetic test values, never real user identifiers and never pre-encoded; a
value that already contains percent-encoded octets is flagged as possible
double encoding. Binding values and stage templates in the report are bounded
by UTF-8 bytes, and `projection` reports any truncation or omitted detail. The
whole Murph result stays within a 96 KiB budget. Inspect the substituted stage
for placement and encoding. Explicitly required macros that are absent, missing
required values, unknown or wrong-case tokens, ambiguous vendor syntax, unsafe
nesting, and missing recipient mappings keep `passed` and `readyForPreview`
false.

Campaigns inherit advertiser trackers dynamically. Add a campaign-local
tracker or disable one inherited default with `save_campaign`:

```json theme={null}
{
  "campaignId": "CAMPAIGN_ID",
  "tracking": {
    "trackers": [
      {
        "name": "Campaign measurement",
        "url": "https://measurement.example/pixel?cb={CACHEBUSTER}",
        "trackerType": "impression"
      }
    ],
    "overrides": [
      {
        "trackerId": "trk_0123456789abcdef01234567",
        "enabled": false
      }
    ]
  }
}
```

Request `include: ["tracking"]` when reading a campaign. Its `tracking` field
contains the local `trackers`, inherited `overrides`, and `effectiveTrackers`
with `sourceScope`, `inherited`, `effectiveEnabled`, and `statusReason`.
Within `tracking`, each supplied array is the complete desired array for that
scope; omit an array to leave that part unchanged.

## Seller tools

| Tool                           | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `save_seller`                  | Patch Seller identity, setup intent, and declared capabilities                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `save_inventory_source`        | Create or update an inventory source, including one existing modular source module's non-secret configuration, without model-visible credentials                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `save_material`                | Register immutable seller source revisions from URLs, uploads, manifests, inline text, or history; reprocess lossless renditions; update metadata, including the seller-governed Library `documentPurpose`; or decide Material-owned candidates. A Material with no stored `documentPurpose` is returned as `uncategorized`; when updating metadata, omit the field to retain the existing category or set it to `null` to clear it. The closed purpose set does not include rate cards or pricing. The `mark_reusable` action sets one non-priced slide, page, or sheet `unitId` reusable; `UNIT_NOT_REUSABLE_KIND` rejects a document container and `UNIT_CONTAINS_PRICING` rejects a unit with commercial figures. This action is gated by the `sales-library` rollout. |
| `save_rfp`                     | Create or revise an RFP turn (including `imported` origin); record free-form feedback with optional typed `grade`, `ledBy`, and `commentary`; release a turn; attach an uploaded or historical-proposal response; endorse it with optional commentary; remove an endorsement; or request and cancel JSON, HTML, PDF, and PowerPoint proposal files. Response-pair actions are gated by the `sales-library` rollout.                                                                                                                                                                                                                                                                                                                                                        |
| `save_library_request`         | Gated by the `sales-library` rollout. `open` creates or returns the one open request for the exact gap, optionally naming its origin RFP turn. `close` records uploaded or dictated seller Material as the closing evidence.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `save_coverage`                | Add or remove declared publisher domains and property claims                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `save_wholesale_product`       | Create or update a wholesale product                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `save_media_kit`               | Deprecated compatibility write for the legacy Business Profile; new clients author the buyer-visible listing through `save_seller`'s `listing` field (`mediaKit` is a deprecated alias for the same field)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `save_playbook`                | Save selling guidance and house-discount declarations                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `save_business_rules`          | Save acceptance and operating rules                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `save_advertiser_instructions` | Save instructions scoped to a buyer or advertiser relationship                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `save_signal`                  | Create or update a seller signal                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `save_work_item`               | Complete or update a typed item from the unified work queue                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `get_delivery`                 | Query seller delivery or cumulative margin; integrated Media Companies can also select Buyer campaign delivery                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `get_rfp_performance`          | Query aggregate, privacy-protected RFP quality, efficiency, and commercial metrics                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |

Seller `search` and `get` kinds include the Seller singleton, inventory sources,
material, RFPs and RFP turns, library requests, coverage, wholesale products, playbook, business rules, media buys,
work items, signals, asks, organization-owned Agents, and other kinds advertised by the live schema. Follow the
[Seller workflows](/v2/setup/v3/seller-workflows) page for sequencing.

### Inventory-source recorded health evidence

Read one source with `get({"kind":"inventory_source","id":"SOURCE_ID",
"include":["diagnostics"]})`. Its compact `health` projection preserves the
recorded `lastErrorAt`, `lastSuccessAt`, and `lastCheckedAt` values from source
diagnostics, including `null`. These are recorded observations, not a new
provider probe made by `get`: when the check timestamp is `null`, freshness is
unavailable rather than assumed to be now. A later recorded success makes an
earlier recorded error historical evidence; it does not erase the original
evidence or prove a transaction succeeded. A successful health observation is
not proof that a media buy was accepted or delivered. To request a new
no-spend check, use the existing `refresh_inventory_source_health` flow.

`search({"kind":"library_request"})` and
`get({"kind":"library_request","id":"LIBRARY_REQUEST_ID"})` are gated by
the `sales-library` rollout. Search accepts `filter.status` (`open` or
`closed`) and `filter.originRfpTurnId`; get returns the request's gap, status,
origin turn, and closing Material evidence.

### Organization-owned Agents

`search({"kind":"agent"})` lists the software Agents the active account's
organization owns. Add `query` to match an Agent ID, Agent display name, or
operator display name. This explicit-only kind does not appear in unrelated
broad object searches and does not turn a buyer counterparty Storefront into an
Agent.

Read one result with `get({"kind":"agent","id":"AGENT_ID"})`. Add
`include:["certification"]` for the complete customer-safe certification
summary, `include:["validationRuns"]` for up to five server-bound run summaries,
or `include:["diagnostics"]` for observed implementation health and the current
production-revision label. A returned run's `diagnosticRead` shows the exact
follow-up: pass its `validationRunId` with `include:["diagnostics"]` to receive
the bounded Activity graph. Sales Agent detail also names its immutable
versioned public `validationSkill`. These reads intentionally omit
credentials, private evidence, operator identifiers, relationship identities,
and owner-only action URLs.

Interactive seller Pages that run inside a host use the account-resolved v3
surface for these reads and writes. In Apostra's hosted bridge, the nested
`/mcp/:toolName` proxy accepts `surface: "v3"` and forwards the call to
`/mcp/v3`; callers still use the tool's canonical arguments, not REST paths.

The [Seller RFP lifecycle](/v2/setup/v3/rfp-lifecycle) guide defines the
`save_rfp` action union, polling states, complete response requirements, and
clarification/failure behavior.

The [RFP performance](/v2/setup/v3/rfp-performance) guide defines aggregate
metric formulas, valid dimensions, disclosure thresholds, and immutable
pagination. Individual RFP facts remain on `get({ kind: "rfp" })`.

Material is evidence and teaching input. It does not replace inventory sources,
products, pricing, Playbook instructions, business rules, signals, formats, or
Seller identity. Use
`get({ "kind": "material", "id": "MATERIAL_ID" })` to inspect the current
source revision, ingestion state, diagnostics, typed candidates, confirmation
receipts, bounded rendition summaries, and source artifact references. Explicit
includes expose independently paginated rendition blocks, reusable visual
assets, extraction diagnostics, composition receipts, and expiring source
access; originals and derived binaries are never embedded in the response.
Confirm a candidate by
calling the exact `proposedMutation.tool` with its arguments: only
Material-owned selling points and stories use `save_material`; every other
candidate uses the save tool named in the suggestion. Review, reject, and
correct do not change Seller configuration. Source records may carry supplied original and
derived structure references such as page or slide order, geometry,
table/image/chart assets, OCR/caption artifacts, digests, provenance,
confidentiality, and reuse rights. Completeness is reported per extraction
facet, and unsupported or degraded parsing is never presented as ready. See
[Seller workflows](/v2/setup/v3/seller-workflows) for upload finalization,
selection, retention, and access behavior.

## MCP App and resource-bound tools

These tools support interactive Pages or resources. A compliant host may keep
some of them out of model context and invoke them only through the associated
app contract.

The Product Marketing Page receives a session-bound capability from
`open_page`. It uses the host-only `teach_search_materials`,
`teach_get_material`, `teach_save_material`, and `teach_renew_capability`
aliases for Material listing and exact reads, seller-private URL and file
additions, upload finalization, Material-owned candidate decisions, and
capability renewal. MCP clients continue to use the public `search`, `get`, and
`save_material` tools. The bridge does not permit generic Material writes or
return source-access or visual-preview URLs. Expiring upload credentials are
returned only for the transient browser `PUT` step.

An active, unexpired Demo can also run the fixed synthetic evaluation from the
Product Marketing Page. These app-only tools require the same session-bound
Teach capability and cannot be invoked as general chat actions:

| Tool                                | Input                 | Result and limit                                                                                                                                                           |
| ----------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `teach_get_evaluation_status`       | `{}`                  | Returns the current `materials_not_loaded`, `review_candidates`, or `ready` state, durable material and pending-candidate counts, and the synthetic-use disclosure.        |
| `teach_prepare_synthetic_materials` | `{ clientRequestId }` | Prepares the fixed six-source synthetic teaching set. An `incomplete` response lists material IDs already saved; retrying continues the same generation.                   |
| `teach_start_synthetic_evaluation`  | `{ clientRequestId }` | After every candidate is reviewed, starts the server-selected held-out brief and returns its exact evaluation RFP and turn. The caller cannot supply or replace the brief. |

All results are rehearsal-only. Synthetic rate-card figures are planning
examples, not live prices or buyer-quotable rates. Custom fulfilment requires a
named human or Connect follow-up; the evaluation does not make it automated or
available now. See [Controlled synthetic evaluation](/v2/storefront/product-marketing/overview#controlled-synthetic-evaluation)
for the full seller workflow.

The Library Page uses the host-only `library_*` aliases the same way, including
`library_get_rfp_turn` and `library_open_proposal_pass` for opening the
authorised Proposal Pass of a response from the Usage view; see
[MCP Apps](/v2/reference/mcp-apps#page-capability-and-tool-bridge) for the
full list.

| Tool                                     | Account | Purpose                                                                                                                                                                                                                                                                                                                                                   |
| ---------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `create_feed_modular_inventory_source`   | Seller  | Open the modular-source creation Page                                                                                                                                                                                                                                                                                                                     |
| `seller_approvals_page`                  | Seller  | Operate approval actions that remain interactive                                                                                                                                                                                                                                                                                                          |
| `get_modular_inventory_source_readiness` | Seller  | Read resource-bound modular readiness                                                                                                                                                                                                                                                                                                                     |
| `prepare_modular_source`                 | Seller  | Prepare a modular source from its Page flow                                                                                                                                                                                                                                                                                                               |
| `get_inventory_feed_context`             | Seller  | Read inventory-feed context                                                                                                                                                                                                                                                                                                                               |
| `preview_inventory_feed`                 | Seller  | Validate and preview an inventory feed                                                                                                                                                                                                                                                                                                                    |
| `commit_inventory_feed`                  | Seller  | Commit a reviewed inventory feed                                                                                                                                                                                                                                                                                                                          |
| `seller_notification_page`               | Seller  | Open Seller notification settings                                                                                                                                                                                                                                                                                                                         |
| `buyer_notification_page`                | Buyer   | Open Buyer notification settings                                                                                                                                                                                                                                                                                                                          |
| `get_buyer_sellers_page`                 | Buyer   | Temporary compatibility read for cached Media Partners Page bundles                                                                                                                                                                                                                                                                                       |
| `get_buyer_advertisers_page`             | Buyer   | Load the Advertisers Page's account landing projection                                                                                                                                                                                                                                                                                                    |
| `buyer_sellers_page`                     | Buyer   | Temporary compatibility write for cached Media Partners Page bundles                                                                                                                                                                                                                                                                                      |
| `open_add_advertiser`                    | Buyer   | Open the advertiser setup Task from the Media Partners Page                                                                                                                                                                                                                                                                                               |
| `set_storefront_operator_domain`         | Seller  | Save the brand domain from the Listing page                                                                                                                                                                                                                                                                                                               |
| `get_discovery_publication`              | Seller  | Read Discovery visibility, publish state, and OpenAI Apps verification status                                                                                                                                                                                                                                                                             |
| `publish_discovery`                      | Seller  | Set Discovery visibility to Apostra or Public and publish it (Public requires a verified public listing domain, gated by rollout). The Page's separate, confirmed Unlist action sends `private`, publishing a private revision that removes the storefront from Apostra and the public web; `private` is a state action, not a third distribution choice. |
| `save_discovery_hostname`                | Seller  | Save or replace the storefront's public listing domain                                                                                                                                                                                                                                                                                                    |
| `activate_discovery_hostname`            | Seller  | Verify or re-verify the storefront's public listing domain                                                                                                                                                                                                                                                                                                |
| `download_discovery_printable_card`      | Seller  | Page-only browser download of a verified printable listing PDF from the current published revision                                                                                                                                                                                                                                                        |
| `update_discovery_openai_challenge`      | Seller  | Set or remove the OpenAI Apps verification token                                                                                                                                                                                                                                                                                                          |
| `probe_discovery_openai_challenge`       | Seller  | Re-check OpenAI Apps verification status on demand                                                                                                                                                                                                                                                                                                        |
| `get_chatgpt_app_config`                 | Seller  | Read the ChatGPT app's listing projected from the seller's listing, hostname/DNS state, verification-token status, and submission-bundle availability                                                                                                                                                                                                     |
| `save_chatgpt_app_listing`               | Seller  | Save the ChatGPT app's category and icons — name, subtitle, description, and every URL project read-only from the seller's listing                                                                                                                                                                                                                        |
| `save_chatgpt_app_hostname`              | Seller  | Save the ChatGPT app's custom hostname                                                                                                                                                                                                                                                                                                                    |
| `activate_chatgpt_app_hostname`          | Seller  | Verify DNS/TLS ownership and activate the ChatGPT app's hostname                                                                                                                                                                                                                                                                                          |
| `save_chatgpt_app_verification_token`    | Seller  | Set or remove the ChatGPT app's own OpenAI Apps verification token                                                                                                                                                                                                                                                                                        |
| `probe_chatgpt_app_verification_token`   | Seller  | Re-check the ChatGPT app's verification status on demand                                                                                                                                                                                                                                                                                                  |
| `get_chatgpt_app_bundle_url`             | Seller  | Mint a fresh, short-lived signed submission-bundle download URL, run at click time                                                                                                                                                                                                                                                                        |
| `approve_chatgpt_app_listing`            | Seller  | Record the app owner's explicit approval of the projected listing, and optionally acknowledge a legacy-listing reconciliation conflict                                                                                                                                                                                                                    |

The Buyer Page companion tools in this table are app-only and are not part of
the model-visible buyer catalog.
Current Media Partners Page bundles use public V3 `search`, `get`, `save_connection`,
and `save_ask`; the two closed Media Partners Page facades remain only for a rolling
deploy window. Current Campaigns bundles instead use the resource-bound
app-only `buyer_campaigns_page_search` and `buyer_campaigns_page_get` tools,
backed by canonical V3 projections; Advertisers uses its closed
landing-projection read. None of these Pages receives the generic `api_call`
surface.

Except for `download_discovery_printable_card`, the Listing and ChatGPT
destination tools above are shared between the model-visible seller catalog
and the Listing page (see [Seller Pages](/v2/setup/seller-pages)). They use
the same account, administrator, and confirmation checks in both paths. The
printable listing tool is app-only because it returns PDF bytes for a browser
download; print fulfillment and provider orders remain outside the tool.

## Core read examples

List objects:

```json theme={null}
{ "kind": "campaign", "query": "autumn", "limit": 25 }
```

Read one object with related detail:

```json theme={null}
{
  "kind": "campaign",
  "id": "CAMPAIGN_ID",
  "include": ["mediaBuys", "creatives", "tracking"]
}
```

Campaign `proposals`, `recentActivity`, `deliverySummary`, and `sourceIdentity`
are accepted include names so the API can return a precise
`unavailableIncludes` explanation, but they are not populated in the preview.

Search documentation:

```json theme={null}
{
  "query": "how does publisher authorization work?",
  "sources": ["docs"]
}
```

Read a selected documentation result by its returned `document` path:

```json theme={null}
{
  "query": "how does publisher authorization work?",
  "document": "storefront/inventory-sources/publisher-properties-coverage",
  "sources": ["docs"]
}
```

Arbitrary web URLs and internal repository paths are not readable through the
documentation search contract.

## Read structured results

Every tool returns MCP `content` for a person or model to read. Integrations
should make decisions from `structuredContent`. `tools/list` is authoritative
for inputs and publishes an output schema where the current tool declares one;
the patterns below cover the remaining preview tools.

| Tool family           | Stable result pattern                                                                                                                                                                       |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `search`              | A kind-specific object block with `results`, counts, `hasMore`, and an opaque `nextCursor` when another page exists                                                                         |
| `get`                 | `{ kind, object }`, plus requested related data and `unavailableIncludes` when an accepted include cannot be populated                                                                      |
| `save_<noun>`         | `action` plus the saved noun or its ID; versioned nouns return the revision needed for the next guarded write                                                                               |
| `request_proposals`   | `executionId`, `status`, a full-cohort summary, one bounded `perSeller` page, and `page.nextCursor`; each outcome carries Proposal IDs, a `productQueryId` and products, or a bounded error |
| `get_delivery`        | Report rows, coverage and finality metadata, warnings, and pagination state                                                                                                                 |
| `get_rfp_performance` | Grouped metric datums, population, freshness, provenance, finality, disclosure semantics, and immutable pagination                                                                          |

Do not parse IDs, revisions, actions, or recovery guidance out of rendered
prose. A write may return `action: "unchanged"`; that is a successful
idempotent outcome. On partial work, preserve `partialWrite`, `warnings`,
`errors`, per-seller or per-source outcomes, and any returned current revision.
`request_proposals.status` is normally `running` while durable background work
contacts the cohort; retry that same key until it becomes `complete`,
`partial`, or `failed`. A buyer can have only one running proposal execution
across campaigns. Terminal result cursors can continue a product-heavy outcome
for the same seller, and `detailsTruncated` marks byte-bounded product fields.
When a seller cannot execute an exact age range, its proposal remains available
as a counter-pitch and its semantic document names the limitation and the
seller's supported ranges. A direct `get_products` response carries the same
information in its additive `limitations` array: `code`, buyer-readable
`message`, requested `min`/`max`/`include_unknown`, and `supported_ranges`.

### AI-8599 bounded targeting continuation (cursor protocol v1)

Capability details can exceed a model-safe response page. V3 therefore exposes
two signed, opaque continuation fields; they are discovery/readback only and
do not contact a seller or start a new proposal round.

| Call                                               | Field                                                   | Use                                                                                                                                                                                 |
| -------------------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `request_proposals`                                | `capabilityCursor` (optional, maximum 4,096 characters) | Continue capability details from the same completed idempotent request. Reuse the original campaign, expected revision, and idempotency key; do not combine it with `resultCursor`. |
| `get({ kind: "proposal", include: ["products"] })` | `proposalProductsCursor` (optional, 1–2,048 characters) | Continue that Proposal's product/capability text at the same revision. This field is invalid for every other kind or without `include: ["products"]`.                               |

Cursors are account- and snapshot-bound. A forged, stale, or wrong-scope cursor
returns a validation error; a Proposal's expiry remains explicit in its
read-only `state` and never gives a cursor execution authority. Follow a
continuation only while the response supplies its next cursor. A terminal page
has `complete: true` and no next cursor. A capability marked unavailable or
blocked is not complete actionable targeting: demographic selections remain
rejected before seller dispatch while the application seller transport remains
on AdCP 3.1. If a signed continuation cannot fit its approved field limit, V3
omits the token and marks the capability unavailable rather than truncating its
scope or presenting omitted details as complete.

For example, a successful advertiser creation returns the new identity under
the noun rather than as an unlabelled string:

```json theme={null}
{
  "action": "created",
  "advertiser": {
    "advertiserId": "ADVERTISER_ID",
    "name": "Acme Europe",
    "primaryCurrency": "EUR"
  }
}
```

`request_proposals` returns enough state to continue each seller branch:

```json theme={null}
{
  "executionId": "EXECUTION_ID",
  "status": "partial",
  "perSeller": [
    {
      "sellerId": "10",
      "status": "quoted",
      "proposalIds": ["sfp1:QUALIFIED_PROPOSAL_ID"]
    },
    {
      "sellerId": "24",
      "status": "failed",
      "error": { "code": "UPSTREAM_ERROR", "message": "..." }
    }
  ],
  "summary": {
    "sellersRequested": 2,
    "sellersQuoted": 1,
    "sellersWithProducts": 0,
    "sellersResponded": 1,
    "sellersFailed": 1,
    "sellersPending": 0
  },
  "page": { "returned": 2, "total": 2, "hasMore": false }
}
```

These examples show the continuation fields, not every property. Treat the
live output schema as authoritative as the preview evolves.
Follow every `page.nextCursor` before treating the review as complete.

## Errors and result handling

Tool errors use bounded structured codes and explanatory text. Common classes
include validation errors, access denial, not found, revision conflict,
unsupported preview behavior, external failure, and partial results.

* Treat `isError: true` as a failed tool result even when the MCP transport
  itself returned successfully.
* Preserve structured IDs and recovery fields; do not parse prose to recover an
  identifier already present in `structuredContent`.
* A partial result is not an empty result. Read its warnings, coverage, and
  per-source or per-seller outcomes.
* Re-read after any response that reports a partial write.

For transport-wide behavior, see [Errors](/v2/reference/errors),
[Rate Limits](/v2/reference/rate-limits), and
[Preview limitations](/v2/setup/v3/limitations).
