> ## 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.

# Product composition

> Products assembled to a buyer's brief on demand by the Merchandising Agent, instead of hand-authored one at a time.

<Note title="Beta">
  Product composition is in beta. The behavior below is live for storefronts with
  a ready Storefront-built product path; the surface may evolve before general availability.
</Note>

**Product composition** lets your storefront assemble buyer-ready products on
demand instead of returning a fixed catalog to everyone. When a buyer sends a
brief, the [Merchandising Agent](/v2/concepts/storefront-agent) builds products
for it — selecting from your inventory building blocks, applying your
[operating instructions](/v2/storefront/operating-instructions/overview), pricing
them against your [Playbook pricing](/v2/storefront/playbook/overview), and
returning products shaped for the brief in front of it.

A composed product is indistinguishable from a hand-authored one to the buyer — it
carries a name, description, targeting, canonical `format_options[]`, and pricing. The
difference is that you never had to author it: the agent built it from your rules.

When a composed product is backed by your static avails feed, it also carries
an `ext.scope3_merchandise` block: the actual property each backing avail runs
on, its available delivery for that avail's own window, and any audience
facts your [product marketing](/v2/storefront/product-marketing/overview) has
taught for that channel — all resolved from your own data, never invented.
This block is not exclusive to composition: a `buying_mode: "wholesale"`
response carries the same `ext.scope3_merchandise` block for the same
static-avails-feed products, since it's read from your inventory data
directly rather than produced by composition. See
[the story-first proposal](/v2/storefront/proposal-story#the-plan-specifically-what-youre-buying)
for the full field-by-field explanation.

Composition is deterministic where it matters — the same brief against the same
inventory and rules returns products with stable ids, so a buyer who discovers
again sees the same products rather than a fresh set each time.

## How the product path is selected

Product composition is not a separately priced add-on and has no Storefront-wide
on/off switch. Each inventory source has an explicit product contract:

* **Storefront-built** supplies ingredients that Apostra turns into buyer-ready
  products.
* **Agent-supplied** supplies complete products that Apostra preserves and routes
  back to that Sales Agent.
* **Both product paths** supports both flows; each Source selects one or both.

The Sales Agent provider owns that declaration. Apostra Source pages show it
read-only. Apostra support handles declaration and both-path selection reconciliation;
Storefront operators cannot change the contract with a local toggle.

The Storefront-built path needs these things in place:

1. **A ready wholesale source.** The source must declare and implement the AdCP
   3.1+ wholesale product contract. Ordinary buyer-facing products are never
   guessed to be ingredients.
2. **Author operating instructions.** Composition can't run without rules — an
   active [operating-instructions](/v2/storefront/operating-instructions/overview)
   ruleset tells the agent what to build and how. If a Storefront-built path is
   ready but no active ruleset exists, the storefront auto-pauses and notifies
   you rather than composing blindly.

If diagnostics say the wholesale path is not ready, correct the Agent's product,
property, format, pricing, or execution declaration as reported, then ask Apostra
support to refresh or reconcile the capability. Apostra does not infer a new
mode from catalog contents.

Optionally, add or update your [Rate card document](/v2/storefront/library#add-a-rate-card)
so composed products are anchored to its floors, targets, and ceilings.

<Note>
  An [ad-server-backed source](/v2/storefront/inventory-sources/overview) has a
  fixed Storefront-built contract. An ad server exposes raw
  inventory — ad units and placements — which must be composed into buyer-ready
  products before it can be sold; this is source behavior, not an account toggle.
</Note>

## Provider adapter catalog snapshots

For a storefront connected directly to a supported provider, each successful
wholesale discovery refreshes a durable snapshot of every product the adapter
returned. The seller product list combines that snapshot with products from any
managed ad-server sources, using the adapter's stable catalog ids so products do
not appear new merely because a buyer started another discovery session.

The snapshot update runs after discovery and never holds up the buyer response.
That also means a newly connected adapter does not appear in the seller product
list until its first successful wholesale discovery finishes. The snapshot is a
catalog view, not permission to buy: every live buyer request still refreshes
account-specific availability and pricing and receives product ids scoped to
that buyer's provider account.

## What a buyer sees

Composed products are returned in ordinary product discovery. The buyer submits a
brief; the agent returns products (and, where configured, multi-product
**proposals** with budget allocations across them). Each product is priced
server-side and clamped to the `[wholesale floor, seller ceiling]` range, so a
composed price is never below your cost. When a product uses a signal you have
priced, the surcharge is added to that floor; see
[how signal pricing changes a quote](/v2/storefront/esa/signals#how-signal-pricing-changes-a-quote).

### Signals on a composed product

When the agent adds your signals to a product, it also chooses how each one is
used, in AdCP's signal targeting terms:

* A yes-or-no signal, such as an audience segment, is included or excluded.
* A signal with named values, such as a site-section key, is narrowed to the
  values that fit the brief. When the signal lists its values, the agent picks
  only from that list. An open key, such as a domain key, lists none, so your
  ad server checks the chosen values when the media buy is placed.
* A numeric signal, such as an income band, is narrowed to a range inside the
  range you declared.

The agent also decides how the signals combine. Signals that are alternatives
share a group, and the product reaches anyone who matches any one of them.
Separate groups are separate requirements: the audience must match every
group. For example, a product for decision makers at small and mid-sized
businesses puts "decision makers" in one group and the two company-size
segments in another. Excluded signals remove anyone who matches any one of
them. The agent adds a separate requirement only when the brief calls for it,
because each one narrows reach. It never puts one of your key-value signals in
the same group as an audience segment, because Google Ad Manager cannot treat
those as alternatives, and it keeps signals in different selection groups you
declare on a product as separate requirements. The product stores that
selection, and a media buy sends it to your ad server unchanged.

A product that narrows a signal to named values or a range, or that requires
signals together, can only be bought with AdCP 3.2. An AdCP 3.1 media buy
reaches your ad server by signal name only, as one either-or list, so it is
refused with an error instead of being sent with a broader audience. Only
signals from a managed sales agent can carry values or requirements, so the
agent does not offer such a product when any of its signals come from another
kind of source.

Over AdCP 3.2, a Google Ad Manager ad server connection currently accepts
audience segments and keys whose values are chosen per buy. A signal you set up
as one fixed key-value pair, or as a combination of several conditions, is
refused over AdCP 3.2 for now and can be bought only over AdCP 3.1.

A composed product declares the AdCP `channels` of the inventory it combines:
every channel declared by the products and avails it was built from. A product
built from a display product and a CTV product declares both. A composed product
never guesses a channel from its creative formats, so it declares one only when
your source inventory does. Declare channels on your products and avails (see
[Make a product ready for proposals](/v2/storefront/inventory-sources/create-products#make-a-product-ready-for-proposals))
so buyers can find composed products by channel and see their delivery by
channel.

Creative delivery is canonical end to end. The composed product exposes the
selected URL-free `format_options[]`; when a buyer pins one with
`format_option_refs[]`, Apostra resolves it back to the exact source option
or direct `format_kind` and `params` selector. Source compatibility adapters may
normalize an older named format at ingestion, but composed products never expose
that source's legacy URL-based format identity.

That source option must still have the same identifier, format kind, parameters,
and publisher scope when the buyer submits the media buy. If the source product
changed after composition, the storefront rejects the stale selection and asks
the buyer to discover the product again. It never substitutes another option
merely because its dimensions look the same.

## Watching it work

Every composition run is observable. Use
[intelligence runs](/v2/storefront/intelligence-runs/overview) to see what the
agent composed for a given brief and why — the inventory it drew on, the rules it
applied, and the rationale behind each product.

## Related

<CardGroup cols={2}>
  <Card title="Operating instructions" href="/v2/storefront/operating-instructions/overview" icon="list-ol">
    The rules the agent follows when composing products — required for composition.
  </Card>

  <Card title="Playbook pricing" href="/v2/storefront/playbook/overview" icon="tags">
    The pricing facts composed products are anchored to.
  </Card>

  <Card title="Intelligence runs" href="/v2/storefront/intelligence-runs/overview" icon="magnifying-glass-chart">
    See what the agent composed for a brief, and why.
  </Card>

  <Card title="Merchandising agent" href="/v2/concepts/storefront-agent" icon="wand-magic-sparkles">
    How the agent turns inventory + rules into a proposal.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.