Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/build-creative-spend-controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"adcontextprotocol": minor
---

spec(creative): add build_creative spend controls — `max_spend` cap + `mode: "estimate"` dry-run.

Follow-on from the persona/scenario review: fan-out (`max_creatives` × `max_variants`) and refinement produce many independently-billed leaves, and `per_unit` pricing gives a rate but not the unit count in advance — so an autonomous buyer had no protocol brake on spend. Both additions are optional and gated by a new `creative.supports_spend_controls` capability.

- **`mode: "estimate"`** (request) → new `BuildCreativeEstimate` response shape (6th `oneOf` member): a dry run that produces and bills nothing and returns a `cost_low`/`cost_high` band computed against the request's actual inputs, with `basis` (`fixed` exact / `estimated_units` / `cpm_deferred`) and an optional per-leaf breakdown. Advisory/non-binding in this revision.
- **`max_spend: { amount, currency }`** (request) → a hard per-call ceiling: the agent stops before the next leaf would exceed it and returns the partial `BuildCreativeVariantSuccess` with new `budget_status: "capped"` and an advisory `BUDGET_CAP_REACHED` in `errors[]` (every returned leaf real and billed; `items_returned` < `items_total`). First-leaf-over-cap → terminal `BUDGET_CAP_REACHED`; currency mismatch → `INVALID_REQUEST`.
- New error code **`BUDGET_CAP_REACHED`** (distinct from `BUDGET_EXCEEDED`/`BUDGET_EXHAUSTED`), in both `enumDescriptions` and `enumMetadata`.
- New capability **`creative.supports_spend_controls`** (default false).

Deferred to the working group (flagged, not omitted): whether an estimate can be **binding**, and whether a refinement-**loop** bound is a protocol-level session budget vs. a buyer responsibility (documented as buyer-side for now).
26 changes: 26 additions & 0 deletions .changeset/creative-transformers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
"adcontextprotocol": minor
---

spec(creative): add `list_transformers` task + account-scoped creative transformers, and extend `build_creative` for transformer selection and variant/catalog multiplicity.

A **transformer** is the creative analog of a media-buy product: an agent-offered, account-scoped, selectable unit of build capability (a voice, model, style, or director) with a typed configuration surface and per-account pricing. This makes account-specific render configuration — including custom values like cloned voices that exist only for one credential — discoverable from the agent rather than guessed, hung on a global format, or smuggled through `ext`.

Strictly additive. Existing `build_creative` callers are unaffected (all new request fields are optional; the shipped `BuildCreativeSuccess`/`BuildCreativeMultiSuccess` response shapes are unchanged — a new fifth member is added alongside them).

New:
- `list_transformers` task (creative protocol): account-scoped, brief-filterable, paginated discovery. An `expand_params` mode returns account-scoped enumerable option **values** (e.g. your configured voices) on the same tool — no separate options endpoint.
- Core schemas `transformer.json` and `transformer-param.json`.
- `get_adcp_capabilities` → `creative.supports_transformers` discriminator.

`build_creative` extensions:
- Request: `transformer_id` (select one transformer; target format(s) must be a subset of its `output_format_ids`), `config` (typed bag keyed to the transformer's params — agents MUST reject unknown/out-of-range values), `max_creatives` (catalog/item fan-out: N distinct creatives, one per item, with sampling), `max_variants` + `variant_axis` + `keep_mode` (alternatives per creative).
- Response: a new `BuildCreativeVariantSuccess` member — `creatives[]` each carrying `variants[]`, with a `build_variant_id` namespace (distinct from preview `preview_id` and served `variant_id`), per-leaf pricing receipt, and `items_total`/`items_returned`. Best-of-N is variants + `recommended`/`rank`. You pay for all produced variants (`per_unit` × N); a kept variant lazily earns a `creative_id` on trafficking, which flows to `report_usage`. Per-format atomic; per-item non-atomic.

`build_variant` lineage + refinement:
- `build_variant_id` is now the leaf-level lineage anchor (`x-entity: build_variant`): minted per produced variant, distinct from the call-level `build_creative_id`, lazily earning a durable `creative_id` only on trafficking. Untrafficked leaves are billed via the inline per-leaf `vendor_cost` only; `report_usage` reconciliation applies once a leaf earns a `creative_id`.
- Conversational refinement: `build_creative` gains `refine_from_build_variant_id` — re-build a prior leaf with a natural-language instruction in `message` plus an optional `config` delta, returning new lineage-linked variants (each with `parent_build_variant_id`); never a mutation. Composes with `max_variants`/`variant_axis`, mutually exclusive with `max_creatives`. Gated by the new `get_adcp_capabilities` → `creative.supports_refinement` discriminator (`UNSUPPORTED_FEATURE` when unsupported; `REFERENCE_NOT_FOUND` for an unknown/expired ref).

Pricing rides the existing `per_unit` model + inline receipt + `report_usage` unchanged — transformers carry `pricing_options` (reusing `vendor-pricing-option.json`).

Deprecations (deprecated in 3.1, removed at 4.0; SDKs MUST keep honoring them through 3.1–3.x): `Format.input_format_ids`, `Format.output_format_ids`, `Format.pricing_options`, and the `input_format_ids`/`output_format_ids` discovery filters on `list_creative_formats` — all superseded by `list_transformers`, which carries each transformer's own I/O signature and pricing.
10 changes: 10 additions & 0 deletions .changeset/generative-encoding-safe-additions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"adcontextprotocol": minor
---

spec(creative): generative-encoding safe additions — `free_text` params + per-output transformer pricing.

The additive half of the generative-agent (Veo/Imagen) encodings follow-on. The two *normative* rules it pairs with — generation count is owned by `max_variants`/`max_creatives` (never a config param), and `aspect_ratio` rides the format axis — are intentionally left to the working group; only the safe schema bits land here.

- `transformer-param.json` `value_source` gains **`free_text`** (an open buyer-authored string with no closed set — e.g. a `negative_prompt` or style note; `type` MUST be `string`, the closed-set fields MUST be absent) plus an optional **`max_length`**. The description also states that count/quantity knobs MUST NOT be params (count rides `max_variants`/`max_creatives`).
- `vendor-pricing-option.json` gains optional **`applies_to_output_format_ids`** so one creative transformer can price different outputs differently (e.g. a multi-publisher template charging per publisher format); an unscoped option is the default. Additive and inert for non-creative vendors (signals/governance) — **flagged for shared-schema owner ack**.
12 changes: 12 additions & 0 deletions .changeset/transformer-capability-discriminators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"adcontextprotocol": minor
---

spec(creative): add pre-call discriminators for creative-transformer refinement retention and fan-out multiplicity.

Lets a buyer agent know — before sending — what a creative agent supports, instead of probing and handling failures. Additive and optional (all fields default to "unsupported / unbounded"), and the keystone the spend-control and conformance follow-ons build on.

- `get_adcp_capabilities` → `creative.refinable_retention_seconds` (integer): the guaranteed-minimum window a produced `build_variant_id` stays refinable. Replaces the prose-only "agent-defined window" with a machine-readable floor; omit to keep it agent-defined.
- `get_adcp_capabilities` → `creative.multiplicity` (object): `supports_catalog_fanout` + `max_creatives_limit`, `supports_variants` + `max_variants_limit`, and `variant_dimensions[]`. Over-limit `max_creatives`/`max_variants` are **clamped** to the ceilings (shortfall via `items_returned` < `items_total`), not rejected — consistent with `item_limit`'s "use the lesser" rule. Absent means no fan-out.
- `transformer.json` → optional `multiplicity` that narrows the agent-level object per transformer (ceilings ≤ agent, `variant_dimensions` ⊆ agent).
- `build_creative` docs note the clamp behavior on `max_creatives`/`max_variants`.
4 changes: 4 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"docs/reference/migration/pricing",
"docs/reference/migration/geo-targeting",
"docs/reference/migration/creatives",
"docs/reference/migration/creative-transformers",
"docs/reference/migration/catalogs",
"docs/reference/migration/optimization-goals",
"docs/reference/migration/brand-identity",
Expand Down Expand Up @@ -359,6 +360,7 @@
"docs/creative/task-reference/preview_creative",
"docs/creative/task-reference/preview_creative-advanced",
"docs/creative/task-reference/list_creative_formats",
"docs/creative/task-reference/list_transformers",
"docs/creative/task-reference/list_creatives",
"docs/creative/task-reference/sync_creatives",
"docs/creative/task-reference/get_creative_delivery"
Expand Down Expand Up @@ -1298,6 +1300,7 @@
"docs/reference/migration/pricing",
"docs/reference/migration/geo-targeting",
"docs/reference/migration/creatives",
"docs/reference/migration/creative-transformers",
"docs/reference/migration/catalogs",
"docs/reference/migration/optimization-goals",
"docs/reference/migration/brand-identity",
Expand Down Expand Up @@ -1556,6 +1559,7 @@
"docs/creative/task-reference/preview_creative",
"docs/creative/task-reference/preview_creative-advanced",
"docs/creative/task-reference/list_creative_formats",
"docs/creative/task-reference/list_transformers",
"docs/creative/task-reference/list_creatives",
"docs/creative/task-reference/sync_creatives",
"docs/creative/task-reference/get_creative_delivery"
Expand Down
3 changes: 2 additions & 1 deletion docs/accounts/tasks/report_usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ Each record requires `account`, `vendor_cost`, and `currency`. Additional fields
| `impressions` | number | Signals: Yes | Impressions delivered |
| `media_spend` | number | percent_of_media: Yes | Media spend for percent-of-media cost verification |
| `signal_agent_segment_id` | string | Signals: Yes | Signal identifier from `get_signals` |
| `creative_id` | string | Creative: Yes | Creative identifier from `build_creative` or `list_creatives`. Links usage to a specific creative for billing verification. |
| `creative_id` | string | Creative: Yes | Creative identifier from `build_creative` or `list_creatives`. Links usage to a specific creative for billing verification. A `build_creative` variant leaf earns a `creative_id` only when trafficked/added to the library — discarded best-of-N or fan-out variants are never reported here; their charge is the inline per-leaf `vendor_cost` on the `build_creative` response (the authoritative record for untrafficked leaves). |
| `build_variant_id` | string | No | When the reported `creative_id` was promoted from a specific `build_creative` variant leaf, the source `build_variant_id` — lets reconciliation link this record back to the exact produced leaf for audit (`pricing_option_id` alone is not unique across leaves). Omit for creatives with no build-variant lineage. |
| `property_list_id` | string | Property lists: Yes | Property list identifier from `list_property_lists`. Links usage to a specific property list for billing verification. |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the settlement model splits billing across two channels: trafficked leaves earn a creative_id and flow here; untrafficked leaves settle via inline vendor_cost on the build_creative response only. that works for charging, but when a variant IS trafficked and its creative_id lands in report_usage, there's no build_variant_id field on the usage record. billing reconciliation can't link a report_usage entry back to the specific leaf for audit - pricing_option_id echoes back but it's not a unique identifier. seems like build_variant_id should be an optional field on the usage record.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — fixed in 63fc72e. Added an optional build_variant_id to the report_usage usage record so a trafficked leaf's usage entry links back to the exact produced leaf for audit (you're right that pricing_option_id isn't unique across leaves). Kept it optional — creatives with no build-variant lineage just omit it.

## Response
Expand Down
12 changes: 12 additions & 0 deletions docs/creative/specification.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,18 @@ Discover creative formats and their specifications.
- Creative agents MAY include references to other creative agents providing additional formats
- When filtering by `format_ids`, creative agents MUST return only the requested formats

### list_transformers

**Reference**: [`list_transformers` task](/docs/creative/task-reference/list_transformers)

Discover the account-scoped transformers a creative agent offers — the creative analog of media-buy products: agent-offered, selectable units of build capability (voices, models, styles) that you select with `transformer_id` in `build_creative`. Offered only by agents that declare `creative.supports_transformers: true` in `get_adcp_capabilities`.

**Requirements:**
- Creative agents that set `creative.supports_transformers: true` MUST implement `list_transformers`
- Creative agents MUST resolve transformers, their enumerable option values, and pricing for the calling account — including custom values configured for that account (e.g. cloned voices)
- Creative agents MUST return account-scoped option values inline on `params[].options[]` for each `field` named in `expand_params`, and SHOULD omit them otherwise
- When `include_pricing` is true, creative agents that charge MUST include `pricing_options` (the `per_unit` model) on each transformer

### build_creative

**Reference**: [`build_creative` task](/docs/creative/task-reference/build_creative)
Expand Down
Loading
Loading