Skip to main content
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.
The Identity and brands guide 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.

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:
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:
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:

Conversation history

Buyer and Seller members can explicitly search their prior Murph rooms with kind: "conversation":
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 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.
The MCP structuredContent response is limited to the write result:
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 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: 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 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.

Internal Marketplace administration

Directly authenticated Apostra SuperAdmins may also receive two internal tools: 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

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:
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

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 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:
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. 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:
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:
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:
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:
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:
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:
Read it back with:
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.
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:
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

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 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 guide defines the save_rfp action union, polling states, complete response requirements, and clarification/failure behavior. The 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 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: 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 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 for the full list. 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). 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:
Read one object with related detail:
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:
Read a selected documentation result by its returned document path:
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. 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. 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:
request_proposals returns enough state to continue each seller branch:
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, Rate Limits, and Preview limitations.