Skip to main content
There is no separate merchandising document. Merchandising is what your storefront does — the Merchandising Agent selling on your behalf — not a thing you save. Three Pages steer it, split by what kind of fact each one holds: The Page names and read tools now use the same three nouns. Selling Terms is retired: structured prices and floors moved to Playbook, while approval gates moved to AI Business Rules. Business Profile is compatibility storage, not a Page and not a Listing. Product Marketing material — what the industry calls a “media kit,” since it can include audience stats, products, and rates — is a separate input to merchandising; that product is not a V3 noun until its backing store exists. Your saved configuration and version history do not move or reset. In chat and any MCP Apps host, get_discovery_card, get_playbook, and get_business_rules open the portable Pages. get_discovery_card opens the canonical Listing through the media-kit resource URI. The deprecated get_media_kit continues to open the legacy Business Profile through the separate business-profile resource URI, for saved-conversation compatibility only; it is not the Listing Page. On the /mcp/v3 surface, the deprecated compatibility kind behind get_media_kit/save_media_kit is spelled listing (its canonical wire name as of 2026-09-01); media_kit is still accepted as a deprecated alias for the identical, still-deprecated handler. Despite the name, it still returns the legacy Business Profile, not the canonical Listing — use get({ kind: "seller", include: ["listing"] }) (discoveryCard is a deprecated alias) and save_seller (its listing field, with mediaKit as a deprecated alias) for that.
/mcp/v3 is available to every authenticated Seller Account. The get and save_* examples below apply to that preview surface; the active account’s permissions, resource access, and independent capability gates still determine which tools are available. The get_discovery_card, get_playbook, and get_business_rules Pages are also available through the current seller MCP surface.

One fact, one owner

Playbook instruction content cannot duplicate structured Playbook pricing, Buyer Discounts, Listing facts, or AI Business Rules. It may refer to those facts without restating them. A conflicting version is rejected on save rather than left for the agent to reconcile later. Writing “the Nike house gets 12%” into your guidance is the case this catches: the discount is a structured rule with its own field, not a sentence for the agent to interpret. AI Business Rules separates three decisions. Brief Acceptance decides whether an otherwise matching brief should receive a response; Creative Policy judges submitted creative; and Approval Mechanics decides whether and where human review runs. With mediaBuyApproval: manual and no active policy, every media buy is held for review. Listing channels/countries and Playbook guide what the agent presents and how it sells; neither controls the review gate. A seller may separately elect to show Brief Acceptance, Creative Policy, both, or neither as Advertising Policies on the listing. This disclosure selector belongs to AI Business Rules because that is where its source text is edited. Approval mode, approvers, routing, per-buyer overrides, and revision notes are never disclosed. Listing can publish the selected local AI Business Rules text. For a pass-through storefront, that buyer-visible disclosure also includes any independently applicable policy text returned by the connected sales agent, so the Listing never hides a downstream restriction.
Starting from a publisher’s media kit, rate card, property roster, policy, or operations exports? Use the publisher onboarding starter kit to request the complete evidence pack, then put each confirmed fact in the owning Page below.
Listing includes local AI Business Rules. Brief Acceptance is evaluated before eligible work is forwarded to the connected sales agent. Channels and countries still come from the upstream agent’s get_adcp_capabilities portfolio and remain read-only; missing declarations stay unknown and do not match filtered searches. Saving, enabling, and evaluating AI Business Rules is not an IU-rated activity today; other qualifying activity remains governed by the organization’s accepted IU Rate Card.

Reading singleton nouns and the storefront projection

playbook and business_rules are singletons — exactly one per storefront — so get identifies them by kind alone. Listing is not a noun; request it as a projection of the storefront:
include: ["versions"] adds the full version history for playbook and business_rules (an array of { version, isActive, notes, createdAt }); omit it and get returns only what is currently active. There is no separate “list versions” tool — version history is an include, the same way source diagnostics are. The history is deliberately a summary: it carries no content, because a storefront with dozens of 50,000-character versions would return an unusable response. To read one version in full, name it:
That returns selectedVersion with the version’s content, its notes, its ownershipIssues, and a restorable flag. restorable: false means the version restates a fact owned by another canonical surface and would be rejected if you saved it back unchanged; the create-and-activate section below explains why, and what to do about it. version is only served for kind: "playbook". include: ["discounts"] adds the playbook’s brand and operator house discount rules. If a read fails, the key is omitted and an unavailableIncludes entry names it — “no discounts are configured” and “the discount read failed” mean opposite things for what your buyers are paying, so the response never lets you confuse them. include: ["approvalRouting"] on business_rules adds who receives each kind of review work — see Approval routing.

Listing — what buyers see

save_storefront writes the local description and, for a managed storefront, the buyer-visible channels and ISO countries. Pass-through storefronts read channels and countries from get_adcp_capabilities; those fields are read-only locally.
Name, logo, colors, and website are resolved from the verified brand domain and brand.json; link or correct that identity instead of forking it on the listing. Open the Page with:

Listing — four steps and destinations

In the seller UI, this page is labeled Listing (the retired Branding & distribution page redirects here). It has four steps, completed in order:
  1. Identity. Name and logo resolved from the verified brand domain and brand.json; link or correct that identity instead of forking it on the listing.
  2. What you do. Description, buyer-visible channels, accepted countries, and the selected Advertising Policies disclosure. This is shared input for every destination below, not a ChatGPT-only listing.
  3. Listed on Apostra marketplace. A switch included with every account. Live means buyer agents can find the storefront on Apostra; turning it off unlists the storefront everywhere, with a confirmation.
  4. Public distribution. The gate for every app destination. Add the Distribution package (the paid upgrade — see what it costs before committing), then point your domain at Apostra with one CNAME at your registrar; Apostra checks it for you. Once the domain verifies, choose Make it public to take Public distribution live — verifying alone does not publish — and verifying requires the storefront to already have a published Apostra listing (step 3). Apostra first-party storefront is attached to platform-managed hosting automatically and never shows this step.
For an active, unexpired Demo Storefront, only Identity and What you do count towards Demo readiness. Listed on Apostra marketplace and Public distribution remain available but are labelled optional. Making either choice can change where the synthetic listing appears, but the Demo does not publish itself or add a paid Distribution package just to show as ready. Expired and inactive Demos use the ordinary four-step Listing model. There is no segmented Apostra/Public control and no selectable “Hidden” option — a hidden storefront can never be found, so it isn’t a distribution choice. A storefront that hasn’t published yet is shown as a state, Not listed yet, never as a step to pick. An already-listed storefront returns to Not listed yet by turning off the Listed on Apostra marketplace switch (step three), with confirmation. Unlisting takes the storefront off Apostra and the public web; buyers and directories can no longer find it until it’s published again. It does not disable an app still served from its own legacy per-app hostname: that app keeps serving until the hostname is disabled or the app moves to your public listing domain. Below the four steps, Distribution shows the destinations where buyers and the seller’s own team find or reach the listing, as tabs: the Listing card (a downloadable PDF) comes first and its tab is always shown. Download becomes available once your listing is live on Apostra (step 3) — the card always matches what is live, so there is nothing to download before then. App destinations — ChatGPT app and Claude — unlock once Public distribution (step 4) is live; before then, the app-destination tab is dimmed with a note that apps unlock with that step. The messaging destinations — Slack, Microsoft Teams, Discord, and WhatsApp, in that order — are how the account’s own linked, active Apostra members reach Murph, not a buyer-facing surface, so they are never gated by Public distribution: Slack’s tab always shows, either Install available (one click, no setup) or Coming soon. Microsoft Teams, Discord, and WhatsApp become tabs once the account is in early access for branded apps; until then they stay in the + Add destination picker with an Early access chip and a line on what each needs before you commit. Adding a branded destination is a prerequisite-plus-artifact flow: what it needs, then the thing you take away. OpenAI’s own domain-ownership proof (the challenge token OpenAI gives you) is one of the ChatGPT destination’s own steps, not a control on the Public distribution step. See Create a white-label ChatGPT app for the full ChatGPT walkthrough, including what happens on OpenAI’s side after you submit. The remaining controls on the page:
  • Brand domain. Save the domain this storefront operates as. See Domains for what the brand domain does and how alias verification works. Saving is explicit — there is no autosave.
  • Verification status. Instead of an unexplained “Verify” button (that action is Apostra-admin only), the page shows the plain-language reason your domain is pending — for example, that your account has no registered domain yet, or that the registered domain still needs member-email or Apostra approval. This is explanatory text on the page itself, not a link; see Domains for the full rules. Once verified, buyers can trust the identity resolved above.
  • Brand identity. The top card starts with the operator domain and shows where identity came from: a website-hosted brand.json, an AAO-managed identity, a community fallback, or no file. A website-hosted file remains read-only here. When no file exists, the AAO builder offers self-hosting or managed hosting after domain verification. This is optional; channel assets can still be uploaded directly.
  • ChatGPT app destination. Once Public distribution is live, Request access requests reviewer access; after activation, the storefront receives its no-spend reviewer advertiser and Download your OpenAI package appears. Prove domain ownership to OpenAI from the same tab by pasting the token OpenAI gives you. There is no dedicated ChatGPT hostname, app-store category selector, or submission-review checkbox on this platform — Apostra sets the directory category to Business for new registrations (an existing registration keeps its saved category) and shows the value before you download, and OpenAI’s review happens in OpenAI’s own portal. See Create a white-label ChatGPT app for the full walkthrough, including what happens on OpenAI’s side after you submit. Storefronts with icons uploaded before automatic generation was introduced continue serving those existing icons during migration. New registrations use resolved brand identity automatically until the owner uploads both channel-specific icons. An existing app still served from its own legacy per-app hostname keeps its ChatGPT destination (and Claude’s, since the same public endpoint already exists there) reachable before Public distribution goes live: an existing reviewer grant, your token, and the package stay available and maintainable without waiting on step four (requesting a new reviewer grant still needs Public distribution and the Distribution package) — move to your public listing domain there when you’re ready; the app keeps working until then.
  • Claude destination. Once Public distribution is live, the Claude tab confirms that the storefront’s public MCP endpoint is ready and links to Anthropic’s Connector Directory submission walkthrough. OAuth and the storefront’s server-side permissions still decide which accounts and tools each person can access. Anthropic’s policy checks, directory review, and approval remain separate from endpoint readiness. In Anthropic’s Authentication step, select OAuth 2.0 + Dynamic Client Registration; the public MCP endpoint publishes the rest of the OAuth metadata. In Test & launch, choose Open reviewer login in Apostra and paste that email and password into the protected test-setup instructions with these steps: connect the listed MCP URL, complete OAuth with the supplied login, and run get_status to confirm the no-spend System advertiser before testing the remaining tools. The same login can be reopened for OpenAI, Anthropic, and Microsoft while its grant is active. It is not included in a download, repository, support ticket, or model-visible tool result; revoke it from either destination when directory review ends.
Setup operations are shared by agents and the page. An administrator can complete the same guarded setup through the V2 Storefront MCP skill or through these controls. Agents must make at most one mutation per turn and obtain explicit confirmation for visibility changes, reviewer provisioning, replacements/removals, and domain-ownership token changes. The printable card download remains Page-only because its result contains PDF bytes rather than a bounded model response. New setups have no ChatGPT app-specific hostname, app-store category selector, or submission-review checkbox: Apostra sets the directory category to Business for new registrations (an existing registration keeps its saved category) and shows the value before download, OpenAI’s review lives in OpenAI’s own portal, and domain ownership is proven once, on the storefront’s own domain, via update_discovery_openai_challenge / probe_discovery_openai_challenge above. The four per-app tools (save_chatgpt_app_hostname, activate_chatgpt_app_hostname, save_chatgpt_app_verification_token, probe_chatgpt_app_verification_token) remain live but legacy-only: they maintain an existing app that still runs on its own per-app hostname until it moves to the public listing domain, and the page shows those hostname and token controls for such apps. Owner approval of the projected listing still happens, as part of Download package, and covers exactly the values shown there — name, subtitle, description, website, and the directory category shown there. If a durable sync to the storefront fails after the account write succeeds, the page reports that explicitly rather than showing a false “saved” — Apostra is alerted on the failure and the seller is told to retry or contact support.

Rate cards — how you price

Your rate cards hold the current seller prices and packaging terms the Merchandising Agent can use. Keep examples and materials in the Library, and keep acceptance policy in AI Business Rules.

playbook — how you sell

save_playbook has three independent halves you can send together or apart: content (posture and packaging guidance, versioned), pricing (your value-anchor facts), and discounts (your brand and operator rate-card rules).
ownershipIssues identifies any line in the active guidance that duplicates structured Playbook pricing, Buyer Discounts, Listing facts, or Business Rules. pricing.facts replaces the whole list — a fact you don’t repeat in the request is removed, the same “whole document” semantics as content.

Discounts are per rule, not a whole list

discounts is the exception, and the difference is deliberate. A house discount is keyed by (houseDomain, scope) and changes what a real buyer pays on their next buy, so a rule you don’t mention is left exactly as it was. Removing one means naming it:
  • A rule for a (houseDomain, scope) pair that doesn’t exist yet is created; one that does is updated. Sending a rule that already matches is a no-op and says so.
  • Omitting notes keeps the note you already wrote. Send null to clear it.
  • remove needs both halves of the key, because nike.com can carry a brand discount and an operator discount at once and they are different rules.
  • Naming the same pair in rules and remove is rejected rather than resolved for you.
Each rule is applied independently. If one fails the response reports exactly which one, keeps the rest, and tells you to retry only the failure — the rows are individually keyed, so a retry repeats nothing that already succeeded. Deciding whether to create or update a rule means knowing whether it already exists, so if your current rules can only be read in part, that question has no safe answer. In that case the affected declaration is refused rather than guessed: you get “this pair could not be confirmed either way” instead of a create that might collide with a live rule, or a “nothing to remove” that would tell you a discount is gone while it keeps applying. Rules the partial read did show are still updated and removed normally. The content half of the same call is never rolled back for a discounts failure, and vice versa: they are separate writes, and the response names what landed.

Which discount a buyer actually gets

The rules you author and the discount a buy receives are different things. search will resolve one for you:
results is your authored rules, unchanged. resolution is derived: the corporate chain the domain resolved up (converse.com → nike.com), the hierarchy coverage, and the nearest rule that keys on each axis. It always carries caveats, because a real buy resolves its brand and its operator separately and takes the larger of the two — one domain’s chain cannot decide it. When the hierarchy can’t be resolved, you get unavailable and your authored rows rather than a confident “no discount applies”.

business_rules — what you accept

save_business_rules also has independent versioned policy sections and two approval gates. Structured policy writes always send both sections, so an omitted field can never erase the other section accidentally.

Turning off human review

Moving creativeApproval or mediaBuyApproval to auto needs acknowledgeNoHumanReview: true on the same call:
Leave it out and the save fails with a validation error rather than a silent no-op:
auto removes human review; it does not remove the product-discovery policy boundary. A confident policy conflict returns no products, while an unavailable qualifier fails open. Once a buyer passes discovery, create_media_buy preserves the established auto-forward contract. With mediaBuyApproval: manual, the submitted buy is evaluated again: clearly on-policy buys auto-forward, while uncertain results, evaluator errors, and deterministic hard findings queue for an operator. Live auto-rejection requires a separate seller opt-in or versioned rollout.

Every save is create-and-activate

playbook.content and business_rules.content are versioned and immutable. Saving one creates a new version and makes it active in the same call — the response’s createdVersion and replacedVersion tell you what happened, and there is no activate flag on the save. A version you save has full effect immediately; there’s no draft state where it exists but hasn’t taken effect.

Rolling back: two routes, and the agent only has one of them

There is no activation field or activate_* tool on /mcp/v3, by design. An agent rolls back the way it writes anything else — by saving the earlier content again, which mints a new version carrying it. History stays append-only, and the restored content is re-checked against the current ownership rules on the way in. The AI Business Rules Page has the other route: it lists every immutable version and can reactivate one directly, behind a two-step confirmation, without minting anything. Use it when you want the version count and history left exactly as they are. Open it with get_business_rules, or on /mcp/v3 with open_page and page: "business_rules". Either way, decisions already recorded keep the policy they were made under. Changing which version is active changes what happens next; it does not retroactively re-judge a creative or media buy someone already approved. playbook works the same way: save the earlier content again to roll back. For the playbook that is a two-step journey, and both steps are reads you can make:
1

Read the version you want back

get({ kind: "playbook", version: 2 }) returns that version’s full content plus a restorable flag.
2

Save that content again

save_playbook({ content: "…" }) mints a new version carrying it, active immediately. Your history keeps both — nothing is overwritten.
restorable: false is worth reading before you try. Playbook content that restates a price, a discount, a market, or an acceptance rule is rejected on save because another canonical surface owns that fact, and an older version written before that check can still contain one. Re-saving it puts it back through the current validator, so a flagged version needs editing before it can go live again. That is not a cost of the re-save route: the older activate call re-runs the same validator and refuses a flagged version too, so the content is unacceptable either way. Edit the flagged lines out and save what’s left. save_playbook and save_business_rules also each take unversioned halves in the same call — pricing and discounts on the playbook, the approval gates on business rules. The parts run in a fixed order — content, then pricing, then discounts — and there is no transaction spanning them. If a part fails, the response tells you exactly where the call stopped:
  • Parts that already succeeded stay applied. A version that was created is live and is not rolled back, and the error still names createdVersion (and replacedVersion) so you know it exists before retrying.
  • Parts that never ran are listed in unattempted, and the message names them. A pricing failure means your discounts were not read, created, changed or removed at all — so the retry guidance says to re-send pricing and discounts, and a content failure means neither of the other two was attempted and nothing is half-applied.
  • Retry only what the response tells you to. Sending content again would mint a second, identical version rather than fixing anything.

Approval routing: who reviews, as opposed to whether anyone reviews

The approval gates decide whether a human reviews. Approval routing decides which human — the named users and roles who receive each kind of review work, the channels they are notified on, and the reminder and escalation clock. It is a third thing, on its own lifecycle: not versioned like the acceptance policy, and not a gate. Read it as an include on business_rules:
An approval kind missing from policies is not unrouted — it falls back to the default org-admin audience. An approver shown with eligible: false is still named in the policy but no longer has an eligible role on the account, which is worth cleaning up. Reading routing requires a storefront admin; a non-admin session gets an approvalRouting entry under unavailableIncludes explaining why, never an empty table that would read as “nobody reviews”.

Routing is changed on the Approvals Page, not by a tool

There is no routing field on save_business_rules, and passing one returns an error naming the Page instead. Choosing an approver changes who can act on your storefront, and that class of change is a human ceremony rather than something an agent writes — the same rule that keeps credentials, access grants, and payout destinations off the agent surface. Open the Page with open_approvals; the routing editor lives there alongside the queues, and only admins can save it. Eligible users’ email addresses are deliberately not returned to the agent. An approver who has never set a name comes back as "name": null rather than falling back to their address — they are still eligible: true, there is just no name safe to show. The Page shows the full directory to the human choosing.

Searching version history and discount rules

playbook and business_rules are singletons, so search doesn’t search them. Listing is a storefront projection rather than a searchable noun. What is list-shaped is the version history and the playbook’s discount rules.
business_rules_version works the same way over acceptance-policy history, and house_discount lists the playbook’s brand and operator rules. Omit kind and search lists every list-shaped object it knows about, playbook_version, business_rules_version, and house_discount included — so a broad question about your setup never silently leaves your discounts out.

Prepare inventory source inputs

Collect the evidence that powers Listing, Playbook, AI Business Rules, and source operations.

v3 Agent Surface

The one-endpoint, noun-shaped surface these tools live on.

Merchandising

The three Pages you use today, and how they map to the platform.

AI Business Rules

How the pre-screen reads your policy, and the three verdicts it returns.

Glossary

Component, Product, and the rest of the vocabulary these nouns build on.