Skip to main content
build_creative accepts inputs the buyer attaches to a build: a selected build capability, render configuration, and — over time — pointers to context the buyer wants the production to honor. This page defines the contract those inputs share so each new pointer inherits it instead of relitigating governance. The one distinction that matters is whether an input is an enforced capability input the agent validates and gates on, or an advisory context pointer that informs production but does not hard-block it at the AdCP layer. The boundary is deliberate and matches the rest of the protocol: capabilities are declared, not gated, and the default is exposure, not coercion. An input gates only when it is a typed contract the agent owns end to end — a render configuration that drives a paid render. Everything else steers.

Two classes of buyer-attached input

Enforced capability inputAdvisory context pointer
Examplestransformer_id, configsignal_ref (#5240), evaluator (#5241), rights / provenance pointers
StatusNormative nowRights/provenance advisory now; signal/evaluator are RFCs
What it doesSelects and parameterizes the build; the agent MUST validate itInforms or steers production toward buyer context
On bad/unknown inputReject with a field-attributed errorDoes not hard-block at the AdCP layer
Where enforcement livesThe creative agent, at build timeElsewhere — the credential layer, trafficking-compatibility checks, or the seller’s own verification

Enforced capability input — transformer_id + config

A transformer_id selects an account-scoped transformer (discovered via list_transformers) to perform the build. One transformer per call; target_format_id / target_format_ids MUST be a subset of the transformer’s output_format_ids. Render configuration goes in config. config is a typed bag keyed by each param’s field from the transformer’s params[]. It gates, and it gates correctly: per build_creative, the creative agent MUST reject unknown keys and out-of-range values with field-attributed errors rather than silently ignoring them — config drives a paid render. Vendor-specific knobs that are not declared params go in ext, not here. The schema leaves the object open because legal keys are dynamic per transformer, so strict validation is a normative agent obligation. This is the only buyer-attached input that hard-blocks a build at the AdCP layer, and it is the right one to: config is a contract the creative agent owns, priced per account, and a mis-keyed value would charge for the wrong render. The agent declares creative.supports_transformers to expose this surface, and transformer and build_variant are registered x-entity types.

Advisory context pointers — signal_ref, evaluator, rights / provenance

Advisory pointers carry buyer context the production SHOULD honor — an audience signal, an evaluator’s preference, a rights or provenance reference. They inform or steer; they MUST NOT hard-block production at the AdCP layer. Where enforcement exists for the thing a pointer names, it lives in the layer that owns it, not in build_creative:
  • Rights / provenance — enforced by the credential layer (generation_credentials at synthesis, rights_constraint.verification_url for live revocation), not by the buyer’s pointer.
  • Signals (#5240) — where enforcement exists, it lives in trafficking-compatibility checks.
  • Evaluators (#5241) — explore-and-rank guidance, surfaced as recommended / rank on leaves; not a gate.
This mirrors the register the protocol already uses for advisory state: buyers MUST NOT gate downstream spend or package activation on an advisory value, the way sync_creatives status is “a UI hint and polling-scheduling signal — not a spend-authorization gate.” keep_mode is the same shape on the request side — advisory only, it does not change what is returned or billed.

Shared shape

Every buyer-attached input — enforced or advisory — inherits the same three-part shape. This is normative for transformers today and is the pattern future pointers reuse:
  1. Account-scoped discovery. Options are discovered, not guessed. list_transformers is account-scoped, brief-filterable, and paginated, with an expand_params mode that enumerates an account’s configured option values (for example, the voices provisioned for that account). There is no separate options endpoint — enumeration is a mode of the one discovery task. A future pointer’s catalog is discovered the same way.
  2. Per-account pricing. Capabilities carry pricing_options (reusing vendor-pricing-option.json) resolved per account, echoed per leaf in the result, and reconciled via report_usage. An output that matches no pricing option and has no unscoped default surfaces UNPRICEABLE_OUTPUT.
  3. Result envelope with a stable leaf anchor. build_creative returns each produced leaf with its own build_variant_id — a namespace distinct from a preview_id (preview), a served variant_id (delivery), and the call-level build_creative_id. Lineage, refinement parentage, and the build→delivery learning join all key on this leaf id, and each leaf carries its own pricing receipt. This is the anchor an evaluator’s scores or a signal’s targeting attach to when those pointers land. The response-shape contract and per-leaf field mechanics live in the build_creative task reference.

Rights are advisory at the pointer; enforcement is the credential

A rights or provenance reference attached to a build is an advisory / audit pointer. It does not authorize the build at the AdCP layer, and the creative agent is not obligated to validate a rights token against it. A transformer’s voice_synthesis_ref[].rights_id says this directly:
This is provenance metadata only, not a build_creative rights token.
Enforcement for rights already ships, and it lives in the credential layer — not at the buyer’s pointer:
  • generation_credentials are provider-enforced at synthesis. The rights agent coordinates with the provider to issue a scoped credential; the provider validates rights_key at generation time and is the gatekeeper. The credential is required on the acquired branch of acquire_rights.
  • rights_constraint.verification_url is the live-revocation channel: downstream participants (SSPs, verification vendors) hit it to confirm a grant is active before serving — HTTP 200 if active, 404 if revoked. (approval_status is a manifest-time snapshot, not a live value.)
  • creative-manifest rights[] travels with the creative as informational metadata: “For v1, rights constraints are informational metadata — the buyer/orchestrator manages creative lifecycle against these terms.”
Buyer-attached provenance follows the same seller-publishes / buyer-represents / seller-confirms split — the buyer declares, the seller verifies. A buyer’s verify_agent.agent_url MUST be a canonicalized match of one of the seller’s published creative_policy.accepted_verifiers[]; off-list URLs are rejected with PROVENANCE_VERIFIER_NOT_ACCEPTED. That rejection is the seller enforcing its own allowlist before any outbound call — not the buyer’s pointer gating the build. The buyer’s attached results are supplementary; a seller that requires verification runs its own detection rather than trusting them.
Settled position vs. #5261. A proposal to treat buyer-attached rights_tokens[] as a hard creative-agent gate is resolved against this page. Hard-gating at the buyer pointer re-invents the generation_credentials enforcement that already ships at the synthesis layer plus verification_url live revocation at the serve layer. The pointer stays advisory; enforcement stays in the credential layer. The one real gap is structural, not a missing gate: voice_synthesis carries provider / voice_id / settings but no rights_id back-reference today. Adding that back-reference is a forward item — it would extend the advisory/audit trail, not introduce a build-time gate.

Forward pointers (not yet normative)

The following advisory pointers belong to this contract’s second class but are not yet in the schema. They are referenced here by governance class only — no fields are defined.
  • Signal-driven creative fan-out (#5240, RFC). A buyer fans out over signal_conditions: SignalTargeting[] on build_creative, producing one creative group per condition, each tagged with the signal_condition it is FOR. Advisory at the AdCP layer; trafficking-compatibility (a sun creative MUST NOT serve into a rain-targeted package) is enforced sales-side via SIGNAL_TARGETING_INCOMPATIBLE. #5240 reuses the existing signals-domain SignalTargeting / signal-ref.json — no new creative-context signal_ref is minted — so the creative’s condition and the sales-side package targeting share one signal_ref identity.
Do not mint a SECOND x-entity: signal creative schema. #5240’s resolution is to reuse the existing signals-domain signal-ref.json (x-entity: signal, scope discriminator) and signal-targeting.json for the creative signal_condition rather than create a parallel signal vocabulary — that shared identity is what lets the sales agent match a creative’s condition against package targeting. The signal pointer stays in the advisory class (no build-time gate); enforcement lives at the trafficking boundary.
  • evaluator / evaluator_id (#5241, RFC). A creative evaluator/oracle the buyer attaches so the agent can explore and rank alternatives (the best_of_n dimension is where it would plug in, surfacing recommended / rank on leaves). Advisory — a preference signal, not a gate. No evaluator schema exists yet.
When these merge, they inherit the shared shape above: account-scoped discovery, per-account pricing, and the same result envelope. They do not inherit the enforced class’s gate — that is reserved for typed contracts the creative agent owns, like config.