> ## Documentation Index
> Fetch the complete documentation index at: https://agenticadvertisingorg-snap-format-preview-links.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# build_creative

> build_creative generates, transforms, or retrieves ad creative manifests in AdCP from a natural language brief to production-ready assets.

Transform, generate, or retrieve a creative manifest for a specific format. To render a visual preview of the resulting manifest, pass it to [`preview_creative`](/docs/creative/task-reference/preview_creative). Supports three modes:

1. **Generation**: Create a manifest from a brief or seed assets (`message` + `creative_manifest`)
2. **Transformation**: Adapt an existing manifest to a different format (`creative_manifest` + `target_format_id`)
3. **Library retrieval**: Resolve a `creative_id` from the agent's library into a manifest with ad-serving assets

For generation and transformation, `build_creative` takes a creative manifest as input and produces a creative manifest as output. For library retrieval, provide a `creative_id` (from [`list_creatives`](/docs/creative/task-reference/list_creatives)) and the agent resolves it from its library.

For information about legacy format IDs and how to reference formats, see [Creative Formats - Referencing Formats](/docs/creative/formats#referencing-formats). On the 3.1 canonical path, creative agents advertise buildable outputs through `get_adcp_capabilities.creative.supported_formats[]`; targetable entries SHOULD include a stable `capability_id`, and buyers pass that value as `target_format_id.id` (or `target_format_ids[N].id`) when calling `build_creative`.

## Request parameters

| Parameter                      | Type                                                                            | Required    | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ------------------------------ | ------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message`                      | string                                                                          | No          | Natural language instructions for the creative agent. For generation, this provides creative direction. For transformation, this guides how to adapt the creative. For refinement, this describes the desired changes.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `creative_manifest`            | object                                                                          | No          | Creative manifest to transform or generate from (see [Creative Manifest](https://adcontextprotocol.org/schemas/v3/core/creative-manifest.json)). For pure generation, this should include the target format\_id and any required input assets. For transformation, this is the complete creative to adapt. When `creative_id` is provided, the agent resolves the creative from its library and this field is ignored.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `creative_id`                  | string                                                                          | No          | Reference to a creative in the agent's library. The creative agent resolves this to a manifest from its library. Use this instead of `creative_manifest` when retrieving an existing creative for tag generation or format adaptation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `concept_id`                   | string                                                                          | No          | Creative concept containing the creative. Used to disambiguate when the same `creative_id` exists in multiple concepts.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `media_buy_id`                 | string                                                                          | No          | Buyer's media buy reference for tag generation context. When the creative agent is also the ad server (e.g., CM360), this provides the trafficking context needed to generate placement-specific tags. Omit when the platform generates tags at the creative level (Flashtalking, Celtra). This is the buyer's reference — the seller-assigned identifier from `create_media_buy`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `package_id`                   | string                                                                          | No          | Buyer's package or line item reference within the media buy. Used with `media_buy_id` when the creative agent needs line-item-level context. Omit to get a tag not scoped to a specific package — the ad server may return the same tag regardless.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `target_format_id`             | object                                                                          | Conditional | Single format ID to generate. Object with `agent_url` and `id` fields. For 3.1 canonical creative-agent routing, `id` is the advertised `creative.supported_formats[].capability_id`; legacy named-format IDs remain accepted during the migration window. Mutually exclusive with `target_format_ids` — provide exactly one.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `target_format_ids`            | array                                                                           | Conditional | Array of format IDs to generate in a single call. Each element is an object with `agent_url` and `id` fields. For 3.1 canonical creative-agent routing, each `id` is an advertised `creative.supported_formats[].capability_id`; legacy named-format IDs remain accepted during the migration window. Mutually exclusive with `target_format_id` — provide exactly one. Returns one manifest per format.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `brand`                        | object                                                                          | No          | Brand reference with `domain` field. Resolves brand identity via `/.well-known/brand.json`. Provides brand-level context (colors, logos, tone).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `quality`                      | string                                                                          | No          | Quality tier: `"draft"` (fast, lower-fidelity for iteration) or `"production"` (full quality for final delivery). If omitted, the creative agent uses its own default.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `item_limit`                   | integer                                                                         | No          | Maximum number of catalog items to use **within one creative**. Caps generation cost for catalog-driven formats (e.g. a 6-card carousel built from a 1,000-product catalog). Distinct from `max_creatives`, which fans out across items.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `transformer_id`               | string                                                                          | No          | Select one transformer (discovered via [`list_transformers`](/docs/creative/task-reference/list_transformers)) to perform the build. The requested target format(s) MUST be a subset of that transformer's `output_format_ids`. The transformer's `per_unit` rate is the pricing source for the build.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `config`                       | object                                                                          | No          | Typed configuration bag keyed to the selected transformer's `params[].field` (e.g. `{ "voice": "isaac", "speaking_rate": 1.1 }`). The creative agent MUST reject unknown keys and out-of-range values with field-attributed errors (strict validation) — vendor-specific knobs that are not declared params go in `ext`. Only meaningful with `transformer_id`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `max_creatives`                | integer                                                                         | No          | Catalog fan-out axis: produce up to N **distinct creatives, one per catalog item** (a sample — e.g. 5 of 150). Distinct from `item_limit`, which caps items used *within* a single creative. Mutually exclusive with `refine_from_build_variant_id`. Triggers the `BuildCreativeVariantSuccess` response shape. Supported only when the agent advertises `creative.multiplicity.supports_catalog_fanout`; values above `max_creatives_limit` are **clamped** (shortfall via `items_returned` \< `items_total`), not rejected.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `signal_conditions`            | array                                                                           | No          | Signal fan-out axis: produce one distinct creative **per signal condition**, each kept and trafficked with its own targeting (a rain creative AND a sun creative). Each item is a [SignalTargeting](/docs/signals/specification). Sibling to `max_creatives`; composes with it (catalog × conditions). Advisory at this layer ([#5280](https://github.com/adcontextprotocol/adcp/issues/5280)) — trafficking-compatibility is enforced sales-side via `SIGNAL_TARGETING_INCOMPATIBLE`. Supported only when the agent advertises `creative.multiplicity.supports_signal_fanout`; counts above `max_signal_conditions_limit` are **clamped**. See [Signal fan-out](#signal-fan-out).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `selection_strategy`           | string                                                                          | No          | How the agent samples when `max_creatives` \< eligible items: `audience_relevance` (user-side, ranks by the same `signal_ref` pointers), `contextual_fit` (content-side, same mechanism with a context signal), `performance` (historical delivery), `proximity`, `inventory_priority` (seller-side), or `random` (default). Ordering surfaces on `rank`/`recommended`. Supported set in `creative.multiplicity.selection_strategies[]`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `max_variants`                 | integer                                                                         | No          | Number of alternative renders to produce **per creative** (best-of-N). Default `1`. You pay for every produced variant; keeping one or more is a separate trafficking step. Supported only when the agent advertises `creative.multiplicity.supports_variants`; values above `max_variants_limit` are clamped.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `variant_axis`                 | object                                                                          | No          | Describes the dimension along which variants differ. Object with `dimension` (`voice` \| `theme` \| `best_of_n` \| `transformer_config` \| `custom`), optional `values[]` (explicit values to enumerate along the axis), optional `field` (the `config` param to sweep — required when `dimension` is `transformer_config`), and optional `label`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `keep_mode`                    | string                                                                          | No          | Advisory hint to the agent on how many variants you intend to keep: `"keep_all"`, `"keep_one"`, or `"keep_some"`. Default `"keep_all"`. Advisory only — you are billed for all produced variants regardless. The response echoes `keep_mode_applied` so you have confirmation the hint was received (audit trail for billing disputes).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `evaluator`                    | object                                                                          | No          | **Experimental** ([status](/docs/reference/experimental-status); feature id `creative.evaluator`). Advisory evaluator ([Evaluator Spec](https://adcontextprotocol.org/schemas/v3/core/evaluator-spec.json)) driving a **gate-then-rank** pipeline over the agent's best-of-N: one source form (`exemplars` / account-arranged `evaluator_id` / `agent_url`), an optional hard `feature_requirement[]` **gate** (drop fails — internal pruning of recommended leaves, never a block on an already-produced billable leaf), an explicit `rank_by` ordering (`[{feature_id, direction: maximize\|minimize}]`), and an allowlisted `feature_agent` pointer. Feature discovery uses [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) `governance.creative_features`; the evaluator returns those same feature IDs in `variants[].eval.features[]`. `evaluator_id` is not discovered from that catalog; it is a pre-provisioned account preset. External agents (`feature_agent.agent_url` / `agent_url`) MUST be in the seller's `creative_policy.accepted_verifiers[]` — off-list → `EVALUATOR_AGENT_NOT_ACCEPTED`. Requires `creative.supports_evaluator`; otherwise ignored. Populates the per-leaf `variants[].eval` block. See [`variants[].eval`](#field-descriptions) and [Evaluator authentication](#evaluator-authentication). |
| `refine_from_build_variant_id` | string                                                                          | No          | Refine a prior produced variant: re-build from its `build_variant_id` applying the NL instruction in `message` plus any `config` delta, returning **new** lineage-linked variants (never a mutation). `transformer_id` and target format(s) are inherited from the parent. Composes with `max_variants`/`variant_axis`; mutually exclusive with `max_creatives`. Requires `creative.supports_refinement` — otherwise `UNSUPPORTED_FEATURE`; an unknown/no-longer-retained ref is `REFERENCE_NOT_FOUND` (`error.field` = `refine_from_build_variant_id`). See [Refinement](#refinement).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `mode`                         | string                                                                          | No          | `"execute"` (default) produces and bills. `"estimate"` is a **dry run** — produces and bills nothing, returns a `BuildCreativeEstimate` cost band (`cost_low`/`cost_high`) computed against this request's inputs. Requires `creative.supports_spend_controls`. See [Spend controls](#spend-controls).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `max_spend`                    | object                                                                          | No          | Hard per-call spend ceiling `{ amount, currency }`. The agent produces leaves until the next would exceed `amount`, then stops and returns the partial result with `budget_status: "capped"`. Requires `creative.supports_spend_controls`. Caps one call — bound refinement loops buyer-side. See [Spend controls](#spend-controls).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `include_preview`              | boolean                                                                         | No          | When true, requests preview renders alongside the manifest. Agents that support this return a `preview` object in the response. Agents that don't simply omit it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `preview_inputs`               | array                                                                           | No          | Input sets for preview generation when `include_preview` is true. Each entry has `name` (required), optional `macros`, and optional `context_description`. If omitted, the agent generates a single default preview. Only supported with `target_format_id` (single-format) — ignored for multi-format requests.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `preview_quality`              | string                                                                          | No          | Render quality for inline previews: `"draft"` or `"production"`. Independent of the build `quality` — you can build at draft and preview at production, or vice versa. Only used when `include_preview` is true.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `preview_output_format`        | string                                                                          | No          | Output format for preview renders: `"url"` (default) or `"html"`. Only used when `include_preview` is true.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `macro_values`                 | object                                                                          | No          | Macro values to pre-substitute into the output manifest's assets. Keys are universal macro names (e.g., `CLICK_URL`, `CACHEBUSTER`); values are the literal substitution strings. The creative agent translates universal macros to its platform's native syntax. Macros not provided here remain as `{MACRO}` placeholders for the sales agent to resolve at serve time.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `account`                      | [AccountRef](/docs/building/integration/accounts-and-agents#account-references) | No          | Account reference for pricing and billing. When present, the creative agent applies account-specific pricing from the rate card, records the build against the account, and can enforce quotas. Required by creative agents that charge for their services.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `push_notification_config`     | object                                                                          | No          | Operation-scoped webhook configuration for async terminal completion/failure notifications when `build_creative` returns `submitted`. Submitted tasks remain pollable via `get_task_status` whether or not this field is present; agents MUST NOT return `submitted` solely because this field is present.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |

### Pricing response fields

When the creative agent charges and `account` is provided, the response includes pricing fields:

| Field               | Type   | Description                                                                                                                                                                                                                                                                                  |
| ------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pricing_option_id` | string | Which rate card pricing option was applied                                                                                                                                                                                                                                                   |
| `vendor_cost`       | number | Cost incurred for this build. May be 0 for CPM-priced creatives where cost accrues at serve time.                                                                                                                                                                                            |
| `currency`          | string | ISO 4217 currency code                                                                                                                                                                                                                                                                       |
| `consumption`       | object | Structured consumption details — `tokens`, `images_generated`, `renders`, `duration_seconds`. See [`creative-consumption.json`](https://adcontextprotocol.org/schemas/v3/core/creative-consumption.json). Informational for cost verification; `vendor_cost` is the billing source of truth. |

For async builds (`status: "working"` with `context_id` polling), pricing fields appear on the final completed response only.

### Recipe identity

Agents MAY return a top-level `recipe_hash` on single-format success responses, and MAY return `variants[].recipe_hash` on multiplicity/refinement leaves. Multi-format `creative_manifests[]` responses do not carry `recipe_hash` in this revision; request the variant shape (`max_variants`) when you need per-output recipe identities. The value is an ETag-style identity for the build-determining inputs: agent-computed, opaque, and scoped to that agent. Buyers can compare it only across responses from the same agent.

`recipe_hash` identifies the input recipe, not the output bytes or legal/disclosure envelope. A nondeterministic build can return different pixels, tags, or variants with the same `recipe_hash`; the value means "same instructions," not "same creative." For fan-out responses, best-of-N leaves produced from the same recipe SHOULD share the same value so clients can group alternatives by their shared source. When both `recipe_hash` and `build_variant_id` appear, `build_variant_id` identifies the output leaf and lineage while `recipe_hash` identifies the input recipe; neither implies the other. The build-to-delivery performance join still uses the lineage identifiers that survive trafficking, not `recipe_hash` alone.

**Important**: Required input assets should be included in the `creative_manifest.assets` object, not as separate task parameters. The format definition specifies what assets it requires. Catalog context for dynamic creatives should be provided via the `creative_manifest.assets` map.

### Evaluator authentication

`build_creative.evaluator` selects or calibrates the evaluator; it does not authenticate the evaluator call. Do not put evaluator API keys, bearer tokens, client secrets, `Authorization` values, JWKs, JWKS documents, or JWKS URIs in `evaluator`, `context`, `ext`, or any other payload field. Credential- or trust-material payload keys are non-conforming and should be rejected with [`CREDENTIAL_IN_ARGS`](/docs/building/by-layer/L3/error-handling#authentication-and-access).

When `evaluator.agent_url` or `evaluator.feature_agent.agent_url` names an external evaluator, the producing creative agent calls that evaluator's [`get_creative_features`](/docs/governance/creative/get_creative_features) endpoint using the normal AdCP transport authentication channel. The evaluator authenticates the creative/seller agent as caller via [RFC 9421 request signing](/docs/building/by-layer/L1/security#request-signing) and JWKS discovery, mTLS, or a pre-provisioned Bearer/API-key credential. Payload fields such as `agent_url`, `account`, `context`, and `ext` are not identity assertions and must not be treated as credentials.

The allowlist check still happens first: an external evaluator URL that is not in `creative_policy.accepted_verifiers[]` is rejected with `EVALUATOR_AGENT_NOT_ACCEPTED` before any outbound call. Buyers learn accepted evaluator URLs from the seller's published `creative_policy.accepted_verifiers[]` where a media-buy/product context exposes one, or from account provisioning in standalone creative-agent integrations. If the URL is on-list but the evaluator is unreachable or rejects the creative agent's transport authentication, the build degrades to seller-default ranking with an advisory `errors[]` note rather than failing the entire build.

### Generation controls

For generative formats, two optional parameters control the generation process:

* **`quality`**: Controls generation fidelity. Use `"draft"` for rapid iteration (reviewing layouts, copy, composition) and `"production"` for final renders. Draft outputs may use lower-resolution images, simplified effects, or placeholder elements. To produce a production version of a draft you like, pass the draft's output manifest back as `creative_manifest` with `quality: "production"`. Note: `preview_creative` also accepts `quality` to control *render* fidelity independently — see [Previewing generative creative](/docs/creative/task-reference/preview_creative#previewing-generative-creative).

* **`item_limit`**: For catalog-driven formats, caps how many catalog items are used during generation. A catalog might contain 1,000 products but you only need 4 hero images. The creative agent selects top items based on relevance or catalog ordering. When `item_limit` exceeds the format's `max_items` (from catalog requirements), the creative agent should use the lesser of the two. If omitted, the creative agent decides based on catalog size and format requirements.

## Use cases

### Pure generation (creating from scratch)

For pure generation, provide a minimal source manifest with the required input assets defined by the format:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
  "message": "Create a banner promoting our winter sale with a warm, inviting feel",
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "display_300x250_generative"
  },
  "brand": {
    "domain": "mybrand.com"
  },
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250_generative"
    },
    "assets": {
      "offering_catalog": {
        "asset_type": "catalog",
        "type": "offering",
        "items": [
          {
            "offering_id": "winter-sale",
            "name": "Winter Sale Collection",
            "description": "50% off all winter items"
          }
        ]
      }
    }
  }
}
```

### Transformation (adapting existing creative)

For transformation, provide the complete source manifest:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "b2c3d4e5-f6a7-4890-b123-456789abcdef",
  "message": "Adapt this creative for mobile, making the text larger and CTA more prominent",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250"
    },
    "assets": {
      "banner_image": {
        "asset_type": "image",
        "url": "https://cdn.example.com/original-banner.png",
        "width": 300,
        "height": 250
      },
      "headline": {
        "asset_type": "text",
        "content": "Winter Sale - 50% Off"
      }
    }
  },
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "display_mobile_320x50"
  }
}
```

### Format resizing

Transform an existing creative to a different size:

```json theme={null}
{
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_728x90"
    },
    "assets": { /* complete assets */ }
  },
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "display_300x250"
  }
}
```

### Library retrieval

Retrieve a creative from the agent's library and resolve it into a manifest with ad-serving assets. Use this when you know the `creative_id` from [`list_creatives`](/docs/creative/task-reference/list_creatives) and want the creative agent to produce a delivery-ready manifest with tags (HTML, JavaScript, or VAST):

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "c3d4e5f6-a7b8-4901-c234-56789abcdef0",
  "creative_id": "ft_88201",
  "concept_id": "concept_holiday_2026",
  "target_format_id": {
    "agent_url": "https://creative.example.com",
    "id": "display_static",
    "width": 300,
    "height": 250
  },
  "macro_values": {
    "CLICK_URL": "https://publisher.example.com/click/abc123"
  }
}
```

**Response** — the manifest includes ad-serving tag assets with macros resolved:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "recipe_hash": "rh_scope3_9f2c7a1d5b3e",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.example.com",
      "id": "display_static",
      "width": 300,
      "height": 250
    },
    "assets": {
      "ad_tag": {
        "asset_type": "javascript",
        "content": "<script src=\"https://cdn.example.com/frameworks/js/sdk.js\"></script><script>AdSDK.createBanner({clickTag:'https://publisher.example.com/click/abc123',width:300,height:250,id:'ft_88201_{CACHEBUSTER}'});</script>"
      },
      "clickthrough_url": {
        "asset_type": "url",
        "url": "https://acmecorp.example.com/holiday-sale?campaign={MEDIA_BUY_ID}"
      }
    }
  }
}
```

The `CLICK_URL` macro was substituted with the provided value; `CACHEBUSTER` remains as a placeholder for the sales agent to resolve at serve time.

Do not use `recipe_hash` to deduplicate ad tags, prove legal/disclosure equivalence, or join build results to served delivery data. Use `build_variant_id` and the promoted `creative_id` for lineage and reporting.

<Note>
  **Cross-agent workflow**: When creative generation and media buying are handled by different agents, use `build_creative` on the creative agent to produce a manifest with tags, then [`sync_creatives`](/docs/creative/task-reference/sync_creatives) on the sales agent to upload it. When the sales agent implements both protocols, this happens on a single endpoint — see [Creative capabilities on sales agents](/docs/creative/sales-agent-creative-capabilities).
</Note>

### Multi-format generation

Generate creatives for multiple formats in a single call using `target_format_ids`. The agent produces one manifest per format from the same source assets and brief:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "d4e5f6a7-b8c9-4012-d345-6789abcdef01",
  "message": "Create display banners for our spring campaign",
  "target_format_ids": [
    {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_static",
      "width": 300,
      "height": 250
    },
    {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_static",
      "width": 728,
      "height": 90
    },
    {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_static",
      "width": 320,
      "height": 50
    }
  ],
  "brand": {
    "domain": "acmecorp.com"
  },
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_static"
    },
    "assets": {
      "banner_image": {
        "asset_type": "image",
        "url": "https://cdn.acmecorp.com/spring-hero.png",
        "width": 1200,
        "height": 628
      },
      "headline": {
        "asset_type": "text",
        "content": "Spring Collection Now Available"
      },
      "clickthrough_url": {
        "asset_type": "url",
        "url": "https://acmecorp.example.com/spring?campaign={MEDIA_BUY_ID}"
      }
    }
  }
}
```

The response uses `creative_manifests` (array) instead of `creative_manifest` (singular). Each manifest is a complete creative manifest with its own `format_id`, ready for `sync_creatives` or `preview_creative`:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "creative_manifests": [
    {
      "format_id": {
        "agent_url": "https://creative.adcontextprotocol.org",
        "id": "display_static",
        "width": 300,
        "height": 250
      },
      "assets": {
        "banner_image": {
          "asset_type": "image",
          "url": "https://cdn.creative-agent.com/generated/spring_300x250.png",
          "width": 300,
          "height": 250
        },
        "headline": { "asset_type": "text", "content": "Spring Collection Now Available" },
        "clickthrough_url": { "asset_type": "url", "url": "https://acmecorp.example.com/spring?campaign={MEDIA_BUY_ID}" }
      }
    },
    {
      "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 728, "height": 90 },
      "assets": { /* same structure, adapted for 728x90 */ }
    },
    {
      "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 320, "height": 50 },
      "assets": { /* same structure, adapted for 320x50 */ }
    }
  ]
}
```

Multi-format requests are atomic — if any format fails (e.g., `FORMAT_NOT_SUPPORTED`), the entire request fails with an error response. The response array order corresponds to the `target_format_ids` request order. Match manifests to requested formats by array position or by comparing the `format_id` on each manifest.

### Multi-format workflow

After a multi-format build, preview all results using `preview_creative` batch mode. Each element of `creative_manifests` in the build response becomes a `creative_manifest` in the batch preview request:

```json theme={null}
{
  "request_type": "batch",
  "quality": "draft",
  "requests": [
    { "creative_manifest": { /* 300x250 manifest from build response */ } },
    { "creative_manifest": { /* 728x90 manifest from build response */ } },
    { "creative_manifest": { /* 320x50 manifest from build response */ } }
  ]
}
```

To refine a single format from a multi-format build, call `build_creative` again with `target_format_id` (singular) and pass back that format's manifest. You don't need to rebuild all formats — just iterate on the one that needs work.

<Note>
  Multi-format requests with `include_preview: true` generate one default preview per format. Custom `preview_inputs` are only supported with single-format requests. For multi-format builds where you need context-specific previews (device variants, different contexts), use a separate `preview_creative` batch call after building.
</Note>

### Transformers & variants

Select a transformer (discovered via [`list_transformers`](/docs/creative/task-reference/list_transformers)) with `transformer_id`, supply a typed `config` keyed to its params, and ask for alternatives with `max_variants`. The agent returns the [variant response](#variant-response): `creatives[]`, each holding a `variants[]` array. Read `recommended` / `rank` to surface the agent's best-of-N pick; traffic the variant(s) you want by their `build_variant_id`.

Resolutions and quality tiers go on `target_format_ids` (or `quality`), not on `variant_axis` — variants are alternatives for the *same* format.

<CodeGroup>
  ```javascript test=false theme={null}
  import { testAgent } from '@adcp/sdk/testing';
  import { BuildCreativeResponseSchema } from '@adcp/sdk';

  const result = await testAgent.buildCreative({
    account: { account_id: 'acct_acme' },
    transformer_id: 'audiostack_voiceover',
    config: { voice: 'isaac', speaking_rate: 1.1, mastering_preset: 'podcast' },
    target_format_id: { agent_url: 'https://creative.audiostack.example', id: 'audio_vo' },
    creative_manifest: {
      format_id: { agent_url: 'https://creative.audiostack.example', id: 'script' },
      assets: { script: { asset_type: 'text', content: 'Discover the new winter collection.' } },
    },
    max_variants: 3,
    variant_axis: { dimension: 'best_of_n', label: 'Read takes' },
    keep_mode: 'keep_one',
    idempotency_key: '0c1d2e3f-4a5b-6c7d-8e9f-a0b1c2d3e4f5',
  });

  const parsed = BuildCreativeResponseSchema.parse(result);
  for (const creative of parsed.creatives ?? []) {
    for (const variant of creative.variants) {
      console.log(variant.build_variant_id, variant.rank, variant.recommended, variant.vendor_cost);
    }
  }
  ```

  ```python test=false theme={null}
  import asyncio
  from adcp.testing import test_agent
  from adcp import BuildCreativeResponse

  async def main():
      result = await test_agent.build_creative(
          account={"account_id": "acct_acme"},
          transformer_id="audiostack_voiceover",
          config={"voice": "isaac", "speaking_rate": 1.1, "mastering_preset": "podcast"},
          target_format_id={"agent_url": "https://creative.audiostack.example", "id": "audio_vo"},
          creative_manifest={
              "format_id": {"agent_url": "https://creative.audiostack.example", "id": "script"},
              "assets": {"script": {"asset_type": "text", "content": "Discover the new winter collection."}},
          },
          max_variants=3,
          variant_axis={"dimension": "best_of_n", "label": "Read takes"},
          keep_mode="keep_one",
          idempotency_key="0c1d2e3f-4a5b-6c7d-8e9f-a0b1c2d3e4f5",
      )
      parsed = BuildCreativeResponse.model_validate(result)
      for creative in parsed.creatives or []:
          for variant in creative.variants:
              print(variant.build_variant_id, variant.rank, variant.recommended, variant.vendor_cost)

  asyncio.run(main())
  ```
</CodeGroup>

You pay for all three takes (`per_unit` × 3); `keep_mode` is advisory. Keeping is a separate trafficking step on the chosen `build_variant_id` — use that id as the `creative_id` when promoting the produced manifest through [`sync_creatives`](/docs/creative/task-reference/sync_creatives). The kept variant then lazily earns a creative record whose `creative_id` is the build leaf id and flows to [`report_usage`](/docs/accounts/tasks/report_usage) and delivery reporting.

### Paid build with pricing

When the creative agent charges and `account` is provided, the response includes pricing fields. The agent selects the applicable pricing option server-side based on the account's rate card and the work performed — the buyer does not pass `pricing_option_id` in the request.

**Per-unit pricing (transformation agent)**:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "e5f6a7b8-c9d0-4123-e456-789abcdef012",
  "account": { "account_id": "acct_acme_creative" },
  "creative_id": "cr_hero_banner",
  "target_format_id": {
    "agent_url": "https://creative.example.com",
    "id": "display_728x90"
  }
}
```

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.example.com",
      "id": "display_728x90"
    },
    "assets": {
      "ad_tag": {
        "content": "<div data-adcp-creative='cr_hero_banner' style='width:728px;height:90px;'>...</div>"
      }
    }
  },
  "pricing_option_id": "po_standard_per_format",
  "vendor_cost": 2.00,
  "currency": "USD",
  "consumption": {
    "renders": 1
  }
}
```

The `pricing_option_id` corresponds to one of the options from [`list_creatives`](/docs/creative/task-reference/list_creatives#pricing). The buyer passes it in [`report_usage`](/docs/accounts/tasks/report_usage) for reconciliation.

**CPM pricing (ad server)** — `vendor_cost` is 0 at build time because cost accrues when impressions are served:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "creative_manifest": { "..." : "..." },
  "pricing_option_id": "po_video_cpm",
  "vendor_cost": 0,
  "currency": "USD"
}
```

## Response format

### Single-format response

When the request uses `target_format_id`, the response contains a single creative manifest:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "recipe_hash": "rh_winter_300x250_4c2a",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250"
    },
    "assets": {
      "offering_catalog": {
        "asset_type": "catalog",
        "type": "offering",
        "catalog_id": "winter-sale"
      },
      "banner_image": {
        "asset_type": "image",
        "url": "https://cdn.example.com/generated-banner.png",
        "width": 300,
        "height": 250
      },
      "headline": {
        "asset_type": "text",
        "content": "50% Off Winter Sale"
      },
      "clickthrough_url": {
        "asset_type": "url",
        "url": "https://mybrand.example.com/winter-sale?campaign={MEDIA_BUY_ID}"
      }
    }
  }
}
```

### Multi-format response

When the request uses `target_format_ids`, the response contains an array of creative manifests:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "creative_manifests": [
    {
      "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 300, "height": 250 },
      "assets": { /* ... */ }
    },
    {
      "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 728, "height": 90 },
      "assets": { /* ... */ }
    }
  ]
}
```

### Variant response

When the request uses `max_creatives`, `max_variants` > 1, `variant_axis`, or `refine_from_build_variant_id`, the agent returns the `BuildCreativeVariantSuccess` shape — the third success shape (`oneOf` member 3 of 6) alongside the single- and multi-format responses, which are unchanged and still used when you build one creative with one variant.

<Warning>
  **No fallback.** If you send any of `max_creatives`, `max_variants > 1`, `variant_axis`, or `refine_from_build_variant_id`, you **must** handle `creatives[]` — you will not get `creative_manifest`/`creative_manifests` back. There is no automatic downgrade to the single/multi shapes.
</Warning>

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "creatives": [
    {
      "build_creative_id": "bc_card_01",
      "catalog_item_ref": { "catalog_type": "product", "item_id": "sku_winter_parka" },
      "variants": [
        {
          "build_variant_id": "bv_card01_a",
          "recipe_hash": "rh_card01_bestofn_7b91",
          "creative_manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_300x250" }, "assets": { "...": "..." } },
          "variant_axis_value": "take_1",
          "recommended": true,
          "rank": 1,
          "pricing_option_id": "po_per_image",
          "vendor_cost": 0.40,
          "currency": "USD",
          "consumption": { "images_generated": 1 }
        },
        {
          "build_variant_id": "bv_card01_b",
          "recipe_hash": "rh_card01_bestofn_7b91",
          "creative_manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_300x250" }, "assets": { "...": "..." } },
          "variant_axis_value": "take_2",
          "recommended": false,
          "rank": 2,
          "pricing_option_id": "po_per_image",
          "vendor_cost": 0.40,
          "currency": "USD",
          "consumption": { "images_generated": 1 }
        }
      ]
    }
  ],
  "items_total": 150,
  "items_returned": 5,
  "vendor_cost": 4.00,
  "currency": "USD"
}
```

*The `creatives[]` array above is abbreviated to one of the 5 returned groups; the aggregate `vendor_cost` of $4.00 covers all 5 groups × 2 variants × $0.40 — it equals the sum of every produced leaf's `vendor_cost`, not just the leaves shown.*

* **creatives\[]**: One entry per built creative group. With `max_creatives`, there is one entry per sampled catalog item (`catalog_item_ref` identifies which item); without catalog fan-out there is a single entry. Treat this as the produced group set; choose among alternatives inside each group's `variants[]`.
* **creatives\[].build\_creative\_id**: Identifies a built creative within this response.
* **creatives\[].catalog\_item\_ref**: Present on catalog fan-out — an object identifying the source catalog item via `item_id` (plus optional `catalog_type`).
* **creatives\[].signal\_condition**: Present on signal fan-out — the SignalTargeting condition this creative group is FOR (e.g. weather=rain). Shares `signal_ref` identity with sales-side package targeting so the sales agent can match and reject incompatible assignments.
* **creatives\[].errors\[]**: Present only on a *failed* catalog item — catalog fan-out is non-atomic, so a failed item is returned as a `creatives[]` entry carrying `errors[]` and no `variants[]`, without failing the batch.
* **creatives\[].variants\[]**: The choose-among alternatives produced for this creative group. Length is at most `max_variants`. Each variant carries its own complete `creative_manifest`.
* **variants\[].build\_variant\_id**: Identifies a single variant — the leaf-level **lineage anchor**. This is its **own namespace** — never reuse a `preview_id` (a `preview_creative` render) or a served `variant_id` (a delivery-time identifier) here. You traffic a chosen build by passing its `build_variant_id`.
* **variants\[].recipe\_hash**: Optional ETag-style identity for the build-determining inputs that produced the leaf. It is agent-computed, opaque, and comparable only within the same agent. Best-of-N leaves from the same source recipe SHOULD share a value. It is useful for cache transparency, cost-avoidance hints, and recipe-level grouping, but it is not output equality, not legal/disclosure equivalence, and not the build-to-delivery join.
* **variants\[].parent\_build\_variant\_id**: Present only on a refined variant ([Refinement](#refinement)) — the `build_variant_id` of the source it was refined from. Absent for first-generation builds.
* **variants\[].variant\_axis\_value**: The value of the `variant_axis` dimension this variant represents (e.g. a voice, a theme).
* **variants\[].recommended** / **variants\[].rank**: Best-of-N signals — `recommended` flags the agent's top pick; `rank` orders the variants. These plus `variant_axis` and `keep_mode` are how best-of-N is expressed.
* **variants\[].eval**: Present only when the request supplied an advisory [`evaluator`](#request-parameters) and the agent advertises `creative.supports_evaluator`. The rank-side of the [`get_creative_features`](/docs/governance/creative/get_creative_features) feature oracle. `eval.features[]` is **`creative-feature-result[]`** — the same shape `get_creative_features.results[]` returns (e.g. a `creative_quality_score` number, an `on_brief` categorical, a calibrated `predicted_performance`); the wrapper is open, each feature item is closed. The agent runs a **gate-then-rank** pipeline over its best-of-N: it evaluates each leaf, drops leaves failing the hard `evaluator.feature_requirement[]` gate from its recommended survivors, then orders survivors by `evaluator.rank_by` (`[{feature_id, direction}]`) — which is what `recommended`/`rank` reflect. The gate is **internal pruning** of which leaves the agent recommends from its own exploration, not an AdCP-layer block: what is produced and billed is governed by `max_variants`/`max_creatives`/`max_spend`, so this block appears only on returned (billed) leaves. Feature discovery uses `get_adcp_capabilities.governance.creative_features`; `evaluator_id` is an account-scoped preset that emits values over that feature vocabulary, not a discovered feature ID and not a separate evaluator feature catalog. The exemplars form calibrates one `predicted_performance` feature in \[0,1]; a pass/warn/fail verdict is a categorical string feature value gated via `feature_requirement.allowed_values` — `eval.features[]` is a feature measurement, never a stored verdict. When the evaluator names an external agent, it must be on the seller's `creative_policy.accepted_verifiers[]` (off-list → `EVALUATOR_AGENT_NOT_ACCEPTED`); the producing agent authenticates its outbound call on the transport, not inside the evaluator payload. Sellers SHOULD populate `eval.calls_used` / `eval.seconds_used` when `agent_url` was used and an `eval_budget` was supplied.
* **variants\[] pricing receipt**: Each variant carries a per-leaf receipt (`pricing_option_id`, `vendor_cost`, `currency`, `consumption`) for the work that produced it. Whenever the build reports cost (the aggregate `vendor_cost` is present), every produced leaf MUST carry its own `vendor_cost` + `currency` (schema-enforced) — leaves are the billing source of truth, and an untrafficked leaf is reconciled from this receipt alone. A CPM-deferred leaf reports `vendor_cost: 0`.
* **items\_total** / **items\_returned**: For catalog fan-out, the total eligible catalog items and how many creatives were returned (e.g. "5 of 150").
* **leaves\_total** / **leaves\_returned**: Leaf-granular counts — total leaves the request would produce vs. how many were actually produced/billed. When `budget_status` is `capped`, `leaves_returned` \< `leaves_total` is the shortfall (works even for variant-only fan-out with no catalog).
* **vendor\_cost** / **currency**: Aggregate cost across all produced variants in the response.
* **keep\_mode\_applied**: Echoes the `keep_mode` the agent applied (present when the request set it). `keep_mode` is advisory and doesn't change what's produced or billed, so this is your confirmation the hint was received — the audit trail for a "I asked for keep\_one but was billed for N" dispute.
* **selection\_strategy\_applied**: Echoes the `selection_strategy` the agent applied (when `max_creatives` sampling occurred). The ranking itself shows in `rank`/`recommended` on the leaves.
* **budget\_status**: `complete` (default; absent == complete) or `capped` — a `max_spend` ceiling stopped production early (a successful partial build, not a failure). See [Spend controls](#spend-controls).
* **errors\[]**: Advisory (non-terminal) entries on an otherwise-successful build — e.g. a `BUDGET_CAP_REACHED` notice when `budget_status` is `capped`.

<Warning>
  You pay for **every produced variant** (`per_unit` × N), not just the ones you keep. Keeping is a separate client act of trafficking the chosen `build_variant_id`(s), typically by using each kept leaf as `creative_id` on [`sync_creatives`](/docs/creative/task-reference/sync_creatives). A kept variant lazily earns a creative record when it is added to the library or first trafficked; that `creative_id` is what flows to [`report_usage`](/docs/accounts/tasks/report_usage) and delivery reporting. An unkept variant is still billed but never earns a `creative_id`.
</Warning>

<Note>
  **Atomicity differs by axis.** Variant production *per format* is atomic — if one variant for a format fails, that format's build fails. Catalog fan-out (`max_creatives`, per item) is **non-atomic**: individual `creatives[]` entries may succeed while others fail. A failed item is returned as a `creatives[]` entry carrying `errors[]` (and no `variants[]`); the batch still succeeds.

  **Build-time variants are not delivery variants.** The `build_variant_id` here is a pre-delivery handle for an alternative you produced. It is a different namespace from the served `variant_id` in [`get_creative_delivery`](/docs/creative/task-reference/get_creative_delivery), which identifies a creative *execution that was delivered*. A build-time variant becomes a delivery variant only once you keep and traffic it.

  **Resolutions and quality tiers are not variants.** Multiple sizes or quality levels belong on the format axis — pass them as `target_format_ids` (or `quality`), not as `variant_axis` values. Variants are alternatives *for the same format* (different voice, theme, or best-of-N take).
</Note>

### Refinement

Conversational refinement re-builds a prior variant with free-form direction — "make it warmer", "tighten the CTA". The typed `config` surface is closed by design, so free-form intent rides the **open** surface that already exists: the `message` field (whose description is already *"For refinement, this describes the desired changes"*). Refinement is therefore not a new task — it's `build_creative` with one extra input:

* Pass `refine_from_build_variant_id` (a prior leaf's `build_variant_id`) plus your instruction in `message` and any `config` delta.
* The agent re-builds from that leaf and returns **new** variants, each with `parent_build_variant_id` set to the source. A refinement is **never a mutation** — the parent leaf is unchanged, and the new leaf earns its own `build_variant_id` (and, on trafficking, its own `creative_id`).
* `transformer_id` and the target format(s) are **inherited** from the parent; you don't repeat them. Passing a `transformer_id` or target format that differs from the parent's is an `INVALID_REQUEST`. `config` is applied as a **delta** over the parent's config.
* Composes with `max_variants` / `variant_axis` (e.g. "three warmer takes" → three refined leaves), but **not** with `max_creatives` / catalog fan-out — you refine one produced creative, not a catalog.
* Requires the agent to advertise `creative.supports_refinement: true` (it retains produced leaves for an agent-defined window). Agents that retain nothing return `UNSUPPORTED_FEATURE`; refine a buyer-held manifest instead via the transform path (`creative_manifest` + `message`). An unknown or no-longer-retained ref returns `REFERENCE_NOT_FOUND` with `error.field` set to `refine_from_build_variant_id`.

Any build can be refined, not just variant builds: a single-format `BuildCreativeSuccess` carries an optional `build_variant_id` (present when the agent supports refinement) that you pass as `refine_from_build_variant_id`. Multi-format builds that need per-output refinable leaves should request the variant shape (`max_variants`), since the bare `creative_manifests[]` array carries no leaf ids.

AI-derivative attribution rides the manifest's existing `provenance`; `parent_build_variant_id` carries only the lineage edge (refinements chain into a tree).

```json test=false theme={null}
{
  "refine_from_build_variant_id": "bv_card01_a",
  "message": "Warmer lighting, and move the logo to the lower-right.",
  "max_variants": 3,
  "account": { "account_id": "acct_acme" },
  "idempotency_key": "7f2a1b3c-4d5e-6f70-8192-a3b4c5d6e7f8"
}
```

### Spend controls

Fan-out and refinement can produce many independently-billed leaves (`max_creatives` × `max_variants`), and `per_unit` pricing gives a *rate* but not the *unit count* in advance (a 6-second voiceover and a 60-second one cost 10× at the same rate). Two opt-in controls, both gated by `creative.supports_spend_controls`:

* **Estimate first (`mode: "estimate"`).** A dry run: the agent produces and bills nothing and returns a `BuildCreativeEstimate` with a `cost_low`/`cost_high` band (and `basis`: `fixed` = exact, `estimated_units` = generative projection, `cpm_deferred` = cost accrues at serve). The band is the load-bearing piece — the seller derives it from your actual inputs, so you don't have to guess unit counts.
* **Cap the call (`max_spend: { amount, currency }`).** A hard stop: the agent produces leaves until the next would push aggregate `vendor_cost` over `amount`, then returns the partial `BuildCreativeVariantSuccess` with `budget_status: "capped"` and an advisory `BUDGET_CAP_REACHED` in `errors[]` — every returned leaf is real and billed, nothing produced is discarded. The leaf-granular shortfall is `leaves_returned` \< `leaves_total` (not `items_returned`/`items_total`, which count catalog items and don't capture a variant-only or mid-item cap); the `BUDGET_CAP_REACHED` advisory is the authoritative cap signal. If even the first leaf would exceed the cap, the call fails with a terminal `BUDGET_CAP_REACHED`. `currency` must match the rate card (no FX) or the request is rejected `INVALID_REQUEST` (`error.field: max_spend.currency`). `max_spend` bounds only **build-time** `vendor_cost` — CPM-priced builds (`basis: cpm_deferred`) are 0 at build time and accrue at serve, so the cap never engages; bound a CPM fan-out with `max_creatives` instead.

They compose: estimate to get `cost_high`, then execute with `max_spend` = `cost_high` × a safety margin (CPM builds excepted — their `cost_high` is 0 at build time). `max_spend` caps a **single call**; to bound an autonomous **refinement loop**, track aggregate `vendor_cost` across calls and stop issuing them (a buyer responsibility in this revision — a protocol-level session budget is deferred to the working group).

<Warning>
  `max_spend` and `mode: "estimate"` require the agent to advertise `creative.supports_spend_controls`; otherwise they're rejected with `UNSUPPORTED_FEATURE`. They're only meaningful with `bills_through_adcp: true` (an out-of-band biller has no AdCP cost to cap against).
</Warning>

### Estimate response

A `mode: "estimate"` request returns the `BuildCreativeEstimate` shape (`oneOf` member 4 of 6) — produced and billed nothing, just a projected cost band:

```json test=false theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "mode": "estimate",
  "estimate": {
    "items_total": 150,
    "items_to_produce": 5,
    "variants_per_item": 3,
    "leaves_total": 15,
    "currency": "USD",
    "cost_low": 6.00,
    "cost_high": 9.00,
    "cost_expected": 7.50,
    "basis": "estimated_units"
  },
  "expires_at": "2026-06-01T00:00:00Z"
}
```

* **estimate.leaves\_total** = `items_to_produce` × `variants_per_item` (× `conditions_total` when `signal_conditions` was sent) — the number of billable leaves `mode: "execute"` would produce.
* **estimate.cost\_low / cost\_high / cost\_expected**: the projected aggregate cost band. The seller derives it from your actual inputs.
* **estimate.basis**: `fixed` (per-format flat — `cost_low == cost_high`, exact), `estimated_units` (generative `per_unit`; the band reflects unit-count uncertainty), or `cpm_deferred` (CPM — build-time cost 0, accrues at serve, so the band is 0).
* **estimate.per\_leaf** (optional): per-leaf breakdown.
* Estimates are **advisory / non-binding** in this revision (binding quotes are deferred to the working group).

### Field descriptions

* **creative\_manifest**: (single-format) The complete creative manifest ready for use with `sync_creatives` or `preview_creative`
* **creative\_manifests**: (multi-format) Array of complete creative manifests, one per requested format. Each contains its own `format_id`.
* **format\_id**: The target format (matches the requested format)
* **assets**: Map of asset keys to asset content — includes creative content (images, text, URLs), catalogs, briefs, and anything else the format requires
* **expires\_at**: Optional ISO 8601 timestamp when generated asset URLs in the manifest expire. Set to the earliest expiration across all generated assets. Re-build the creative after this time to get fresh URLs. Not present when the manifest contains no expiring URLs (e.g., pure text generation or assembly-only transforms).
* **preview**: Optional. Present when `include_preview` was true in the request and the agent supports inline preview. Contains the same content fields as a `preview_creative` single response (`previews`, `interactive_url`, `expires_at`) minus the `response_type` discriminator, so clients can reuse the same preview rendering logic. Preview URLs follow the same durability contract as `preview_creative`: they remain dereferenceable until `expires_at`, or until explicit out-of-band revocation when no expiration is present. For single-format responses, each entry in `previews[]` corresponds to an input set from `preview_inputs`. For multi-format responses, each entry includes a `format_id` and corresponds to one requested format (one default preview per format; `preview_inputs` is ignored).
* **preview\_error**: Optional. Standard error object (`code`, `message`, `recovery`) present when `include_preview` was true but preview generation failed. The `recovery` field indicates whether the failure is `transient` (retry later), `correctable`, or `terminal`. Distinguishes "agent doesn't support inline preview" (field absent, no error) from "preview generation failed" (field present with structured error).

### Compliance errors

If the manifest includes a brief asset with `compliance` requirements that the creative agent cannot satisfy, the agent MUST return an error — not a partial success. Unsatisfied disclosures are a hard failure.

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "failed",
  "errors": [
    {
      "code": "COMPLIANCE_UNSATISFIED",
      "message": "Required disclosure cannot be rendered in this format",
      "field": "creative_manifest.assets.brief.compliance.required_disclosures[0]",
      "details": {
        "disclosure_text": "Past performance is not indicative of future results.",
        "position": "footer",
        "reason": "Format display_mobile_320x50 does not support footer position"
      },
      "suggestion": "Use a format that supports footer disclosures, or change position to 'overlay'"
    }
  ]
}
```

Creative agents MUST validate that all `required_disclosures` can be satisfied in the target format before generating the creative. If any disclosure cannot be placed as specified, the entire request fails. This ensures regulated creatives are never served without required legal text.

## Response timing

How the creative agent responds depends on how long the operation will take:

| Expected duration                                       | Status      | Caller experience                                                                                                                        |
| ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Under 30 seconds                                        | `completed` | Result returned directly — synchronous                                                                                                   |
| Over 30 seconds, server actively processing             | `working`   | Out-of-band status updates while the server continues processing. Caller holds the connection — still synchronous from their perspective |
| Blocked on external dependency (human review, approval) | `submitted` | Truly async — caller should configure a webhook via `push_notification_config` and move on                                               |

The creative agent decides which path to take based on the work involved. Library retrieval is instant. Simple transformations take seconds. AI generation varies — a quick banner might complete in 10 seconds, a complex video composition might take minutes.

### `working` is a progress signal, not a polling trigger

When the server expects to take more than 30 seconds but is actively processing, it sends `working` as an out-of-band MCP status update. This keeps the client informed ("I'm on it") without requiring the caller to switch to a polling or webhook pattern. The connection stays open and the result arrives when it's ready.

### When to go async

`submitted` means the operation is blocked on something outside the server's control:

* **Human creative review** — brand guidelines require approval before returning
* **External approval workflows** — third-party compliance or legal review

For these cases, configure a webhook via `push_notification_config` to receive the result. See [Async Operations](/docs/building/by-layer/L3/async-operations) and [Push Notifications](/docs/building/by-layer/L3/webhooks).

### Human-in-the-loop

The agent may return `status: "input-required"` when it needs human input — for example, when brand guidelines require creative approval or when the agent needs clarification on creative direction:

```json theme={null}
{
  "reason": "CREATIVE_DIRECTION_NEEDED"
}
```

**Reason codes:**

* `APPROVAL_REQUIRED` — Creative needs human approval before finalizing
* `CREATIVE_DIRECTION_NEEDED` — Agent needs clarification on creative brief or direction
* `ASSET_SELECTION_NEEDED` — Agent needs the caller to choose between asset options

## Workflow integration

### Typical generation workflow

1. **Build**: Use `build_creative` to generate/transform the manifest
2. **Preview**: Use `preview_creative` to see how it renders (see [preview\_creative](/docs/creative/task-reference/preview_creative))
3. **Sync**: Use `sync_creatives` to traffic the finalized creative

You can combine steps 1 and 2 by setting `include_preview: true` on the build request. If the agent supports it, the response includes a `preview` object alongside the manifest — same content fields as `preview_creative`, no extra round trip. If the agent doesn't support inline preview, the field is simply absent and you fall back to a separate `preview_creative` call. Always check for the presence of `preview` rather than assuming it will be there when requested.

Use `preview_quality` to control render fidelity independently from build quality. For example, build at `quality: "draft"` (fast concept generation) but preview at `preview_quality: "production"` (full-fidelity render to show stakeholders the layout). If `preview_quality` is omitted, the agent uses its own default.

```json theme={null}
// Build at draft quality, but preview at production quality for stakeholder review
{
  "message": "Create a display banner for our winter sale",
  "target_format_id": {"agent_url": "...", "id": "display_300x250_generative"},
  "brand": { "domain": "mybrand.com" },
  "quality": "draft",
  "include_preview": true,
  "preview_quality": "production",
  "creative_manifest": {
    "format_id": {"agent_url": "...", "id": "display_300x250_generative"},
    "assets": {
      "product_catalog": {
        "type": "product",
        "catalog_id": "winter-products"
      }
    }
  }
}

// Or: Build first, preview separately
// Step 1: Build
{
  "message": "Create a display banner for our winter sale",
  "target_format_id": {"agent_url": "...", "id": "display_300x250_generative"},
  "brand": { "domain": "mybrand.com" },
  "creative_manifest": {
    "format_id": {"agent_url": "...", "id": "display_300x250_generative"},
    "assets": {
      "product_catalog": {
        "type": "product",
        "catalog_id": "winter-products"
      }
    }
  }
}

// Step 2: Preview (using the output manifest from step 1)
{
  "request_type": "single",
  "format_id": {"agent_url": "...", "id": "display_300x250"},
  "creative_manifest": {
    /* output from build_creative - includes all assets */
  },
  "inputs": [{"name": "Desktop view"}, {"name": "Mobile view"}]
}

// Step 3: Sync (if preview looks good)
{
  "creative_manifests": [{ /* approved manifest from build_creative */ }]
}
```

**Key insight**: The manifest carries everything. Briefs, catalogs, images, text — all live in the assets map and flow through from input to output. No need to pass them separately at each step.

## Examples

### Example 1: Pure generation (generative format)

Generate a creative from scratch using a generative format:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "f6a7b8c9-d0e1-4234-f567-89abcdef0123",
  "message": "Create a display banner for our winter sale. Use warm colors and emphasize the 50% discount",
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "display_300x250_generative"
  },
  "brand": {
    "domain": "mybrand.com"
  },
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250_generative"
    },
    "assets": {
      "offering_catalog": {
        "asset_type": "catalog",
        "type": "offering",
        "items": [
          {
            "offering_id": "winter-sale",
            "name": "Winter Sale Collection",
            "description": "50% off all winter items"
          }
        ]
      }
    }
  }
}
```

**Response**:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250"
    },
    "assets": {
      "offering_catalog": {
        "asset_type": "catalog",
        "type": "offering",
        "catalog_id": "winter-sale"
      },
      "banner_image": {
        "asset_type": "image",
        "url": "https://cdn.creative-agent.com/generated/banner_12345.png",
        "width": 300,
        "height": 250
      },
      "clickthrough_url": {
        "asset_type": "url",
        "url": "https://mybrand.example.com/winter-sale?campaign={MEDIA_BUY_ID}"
      }
    }
  }
}
```

### Example 2: Format transformation

Transform an existing 728x90 leaderboard to a 300x250 banner:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "a7b8c9d0-e1f2-4345-a678-9abcdef01234",
  "message": "Adapt this leaderboard creative to a 300x250 banner format",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_728x90"
    },
    "assets": {
      "banner_image": {
        "asset_type": "image",
        "url": "https://cdn.mybrand.com/leaderboard.png",
        "width": 728,
        "height": 90
      },
      "headline": {
        "asset_type": "text",
        "content": "Spring Sale - 30% Off Everything"
      },
      "clickthrough_url": {
        "asset_type": "url",
        "url": "https://mybrand.example.com/spring?campaign={MEDIA_BUY_ID}"
      }
    }
  },
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "display_300x250"
  }
}
```

**Response**:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250"
    },
    "assets": {
      "banner_image": {
        "asset_type": "image",
        "url": "https://cdn.creative-agent.com/resized/banner_67890.png",
        "width": 300,
        "height": 250
      },
      "headline": {
        "asset_type": "text",
        "content": "Spring Sale - 30% Off"
      },
      "clickthrough_url": {
        "asset_type": "url",
        "url": "https://mybrand.example.com/spring?campaign={MEDIA_BUY_ID}"
      }
    }
  }
}
```

### Example 3: Transformation with specific instructions

Adapt a creative for mobile with specific design changes:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "b8c9d0e1-f2a3-4456-b789-abcdef012345",
  "message": "Make this mobile-friendly: increase text size, simplify the layout, and make the CTA button more prominent",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x600"
    },
    "assets": {
      "background_image": {
        "asset_type": "image",
        "url": "https://cdn.mybrand.com/bg.jpg",
        "width": 300,
        "height": 600
      },
      "headline": {
        "asset_type": "text",
        "content": "Discover Our New Collection"
      },
      "body_text": {
        "asset_type": "text",
        "content": "Shop the latest styles with free shipping on orders over $50"
      },
      "cta_text": {
        "asset_type": "text",
        "content": "Shop Now"
      }
    }
  },
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "display_mobile_320x50"
  }
}
```

**Response**:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_mobile_320x50"
    },
    "assets": {
      "banner_image": {
        "asset_type": "image",
        "url": "https://cdn.creative-agent.com/mobile/banner_mobile_123.png",
        "width": 320,
        "height": 50
      },
      "headline": {
        "asset_type": "text",
        "content": "New Collection - Shop Now"
      },
      "clickthrough_url": {
        "asset_type": "url",
        "url": "https://mybrand.example.com/new?campaign={MEDIA_BUY_ID}"
      }
    }
  }
}
```

### Example 4: Generation with creative brief

Generate a creative using structured campaign context via `brand` and a brief asset on the manifest:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "c9d0e1f2-a3b4-4567-c890-bcdef0123456",
  "message": "Create a display banner for the holiday campaign targeting gift shoppers",
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "display_300x250_generative"
  },
  "brand": {
    "domain": "acmecorp.com"
  },
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250_generative"
    },
    "assets": {
      "brief": {
        "asset_type": "brief",
        "name": "Holiday Sale 2025",
        "objective": "conversion",
        "audience": "Holiday gift shoppers aged 25-55",
        "territory": "festive savings",
        "messaging": {
          "headline": "Holiday Deals Are Here",
          "cta": "Shop Now",
          "key_messages": [
            "Up to 50% off select items",
            "Free shipping on orders over $50"
          ]
        },
        "reference_assets": [
          {
            "url": "https://cdn.acmecorp.com/holiday-mood-board.pdf",
            "role": "mood_board",
            "description": "Holiday campaign mood board with festive color palette"
          }
        ]
      },
      "offering_catalog": {
        "asset_type": "catalog",
        "type": "offering",
        "items": [
          {
            "offering_id": "holiday-sale",
            "name": "Holiday Sale Collection",
            "description": "Up to 50% off select holiday items"
          }
        ]
      }
    }
  }
}
```

### Example 5: Generation with compliance requirements

Generate a financial services creative with regulatory disclosures and prohibited claims:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "d0e1f2a3-b4c5-4678-d901-cdef01234567",
  "message": "Create a display banner promoting retirement planning advisory services",
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "display_300x250_generative"
  },
  "brand": {
    "domain": "pinnaclewealth.com"
  },
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250_generative"
    },
    "assets": {
      "brief": {
        "asset_type": "brief",
        "name": "Retirement Advisory Q1 2026",
        "objective": "consideration",
        "audience": "Pre-retirees aged 50-65 with investable assets",
        "territory": "trusted financial guidance",
        "messaging": {
          "headline": "Plan Your Retirement with Confidence",
          "cta": "Schedule a Consultation",
          "key_messages": [
            "Personalized retirement planning",
            "Tax-efficient investment strategies"
          ]
        },
        "compliance": {
          "required_disclosures": [
            {
              "text": "Past performance is not indicative of future results.",
              "position": "footer",
              "jurisdictions": ["US"],
              "regulation": "SEC Rule 156"
            },
            {
              "text": "Securities offered through Pinnacle Wealth Securities, LLC. Member FINRA/SIPC.",
              "position": "footer",
              "jurisdictions": ["US"],
              "regulation": "FINRA Rule 2210"
            },
            {
              "text": "Capital at risk. The value of investments can go down as well as up.",
              "position": "prominent",
              "jurisdictions": ["GB"],
              "regulation": "FCA COBS 4.5"
            },
            {
              "text": "Pinnacle Wealth Advisors is a registered investment adviser.",
              "position": "footer"
            }
          ],
          "prohibited_claims": [
            "guaranteed returns",
            "risk-free investment",
            "outperform the market"
          ]
        }
      }
    }
  }
}
```

Compliance requirements differ by jurisdiction: the US requires SEC-mandated disclosures while the UK requires FCA-mandated risk warnings. The third disclosure (no `jurisdictions`) applies globally. The `prohibited_claims` array tells the creative agent which claims to avoid in generated copy.

### Example 6: Commerce media with brief and product catalog

Generate a sponsored product carousel with campaign context, compliance disclosures, and a synced product catalog:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "e1f2a3b4-c5d6-4789-e012-def012345678",
  "message": "Create a product carousel highlighting the top 4 sale items",
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "sponsored_product_carousel"
  },
  "brand": {
    "domain": "novabrands.com"
  },
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "sponsored_product_carousel"
    },
    "assets": {
      "brief": {
        "asset_type": "brief",
        "name": "Spring Sale 2026",
        "objective": "conversion",
        "audience": "Value-conscious shoppers aged 25-45",
        "messaging": {
          "headline": "Spring Sale — Up to 40% Off",
          "cta": "Shop Now"
        },
        "compliance": {
          "required_disclosures": [
            {
              "text": "Sponsored",
              "position": "prominent"
            },
            {
              "text": "Prices may vary by location. See store for details.",
              "position": "footer"
            }
          ]
        }
      },
      "product_catalog": {
        "asset_type": "catalog",
        "type": "product",
        "catalog_id": "spring_sale_2026"
      }
    }
  }
}
```

The brief and product catalog travel together in the manifest's `assets` map. The format declares both a `brief` and `catalog` asset type — the buying agent discovers this via `list_creative_formats` and syncs the required catalog before submitting.

### Example 7: Build with inline preview

Build a creative and get preview renders in the same response:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "f2a3b4c5-d6e7-4890-f123-ef0123456789",
  "message": "Create a banner for our spring campaign",
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "display_300x250_generative"
  },
  "brand": {
    "domain": "novabrands.com"
  },
  "include_preview": true,
  "preview_inputs": [
    { "name": "Default" },
    { "name": "Dark mode", "macros": { "COLOR_SCHEME": "dark" } }
  ],
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250_generative"
    },
    "assets": {
      "offering_catalog": {
        "asset_type": "catalog",
        "type": "offering",
        "items": [
          {
            "offering_id": "spring-promo",
            "name": "Spring Collection",
            "description": "30% off new arrivals"
          }
        ]
      }
    }
  }
}
```

**Response** (when the agent supports inline preview):

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250"
    },
    "assets": {
      "offering_catalog": {
        "asset_type": "catalog",
        "type": "offering",
        "catalog_id": "spring-promo"
      },
      "banner_image": {
        "asset_type": "image",
        "url": "https://cdn.creative-agent.com/generated/spring_abc123.png",
        "width": 300,
        "height": 250
      },
      "clickthrough_url": {
        "asset_type": "url",
        "url": "https://novabrands.com/spring"
      }
    }
  },
  "preview": {
    "previews": [
      {
        "preview_id": "prev_default",
        "renders": [
          {
            "render_id": "r1",
            "output_format": "url",
            "preview_url": "https://preview.creative-agent.com/abc123/default",
            "role": "primary",
            "dimensions": { "width": 300, "height": 250 }
          }
        ],
        "input": { "name": "Default" }
      },
      {
        "preview_id": "prev_dark",
        "renders": [
          {
            "render_id": "r2",
            "output_format": "url",
            "preview_url": "https://preview.creative-agent.com/abc123/dark",
            "role": "primary",
            "dimensions": { "width": 300, "height": 250 }
          }
        ],
        "input": { "name": "Dark mode", "macros": { "COLOR_SCHEME": "dark" } }
      }
    ],
    "expires_at": "2026-03-13T06:00:00Z"
  },
  "expires_at": "2026-03-13T06:00:00Z"
}
```

The `preview` object contains the same content fields as a `preview_creative` single response (`previews`, `interactive_url`, `expires_at`). If the agent does not support inline preview, this field is absent — the buyer agent falls back to a separate `preview_creative` call. If preview generation fails, the response includes `preview_error` with a standard error object (`code`, `message`, `recovery`) instead.

### Example 8: Draft generation with item limit

Generate a draft-quality creative from a large catalog, capping the number of items:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "a3b4c5d6-e7f8-4901-a234-f01234567890",
  "message": "Create hero images for our top sale items",
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "sponsored_product_carousel"
  },
  "brand": {
    "domain": "novabrands.com"
  },
  "quality": "draft",
  "item_limit": 4,
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "sponsored_product_carousel"
    },
    "assets": {
      "product_catalog": {
        "asset_type": "catalog",
        "type": "product",
        "catalog_id": "spring_sale_2026"
      }
    }
  }
}
```

The catalog may contain hundreds of products, but `item_limit: 4` ensures only 4 hero images are generated. `quality: "draft"` produces fast, lower-fidelity output for review.

**Response**:

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-response.json",
  "status": "completed",
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "sponsored_product_carousel"
    },
    "assets": {
      "product_catalog": {
        "asset_type": "catalog",
        "type": "product",
        "catalog_id": "spring_sale_2026"
      },
      "card_1_image": {
        "asset_type": "image",
        "url": "https://cdn.creative-agent.com/draft/card1_abc123.jpg",
        "width": 400,
        "height": 400
      },
      "card_2_image": {
        "asset_type": "image",
        "url": "https://cdn.creative-agent.com/draft/card2_def456.jpg",
        "width": 400,
        "height": 400
      },
      "card_3_image": {
        "asset_type": "image",
        "url": "https://cdn.creative-agent.com/draft/card3_ghi789.jpg",
        "width": 400,
        "height": 400
      },
      "card_4_image": {
        "asset_type": "image",
        "url": "https://cdn.creative-agent.com/draft/card4_jkl012.jpg",
        "width": 400,
        "height": 400
      }
    }
  },
  "expires_at": "2026-03-02T06:00:00Z"
}
```

The `expires_at` field signals when the generated CDN URLs expire. Re-build after this time to get fresh URLs. Once the direction is approved, re-submit the output manifest with `quality: "production"` for final renders.

## Key concepts

### Brand vs creative brief

|              | Brand                                      | Creative Brief                                                     |
| ------------ | ------------------------------------------ | ------------------------------------------------------------------ |
| **Scope**    | Brand identity                             | Campaign context                                                   |
| **Lifespan** | Stable across campaigns                    | Specific to a campaign or flight                                   |
| **Contains** | Colors, logos, fonts, tone                 | Audience, territory, messaging, compliance, reference assets       |
| **Legal**    | Brand-level disclaimers (always-on)        | Campaign-specific regulatory disclosures (regional, product-based) |
| **Source**   | Brand registry / `/.well-known/brand.json` | Agency or brand team                                               |
| **Lives on** | Resolved via domain lookup                 | The manifest's assets map (`assets.brief`)                         |

Both are optional. `brand` provides stable brand identity (colors, logos, tone) resolved via the domain's `/.well-known/brand.json`. The brief is an asset on the manifest (`assets.brief`), so it travels with the creative through regeneration, resizing, and auditing. The `message` field provides per-request natural language instructions.

**Precedence**: The `brand` parameter is the authoritative source for creative rendering context (colors, logos, tone).

**Layering**: The brief asset on the manifest provides structured direction; `message` on the request provides per-request natural language overrides. When both provide conflicting direction, `message` takes precedence as the most specific instruction.

### Transformation model

`build_creative` follows a **manifest-in, manifest-out** model:

* Input: Creative manifest (can be minimal or complete -- everything lives in assets)
* Process: Transform/generate based on `message` and manifest content
* Output: Target creative manifest ready for preview or sync (brief carries forward)

### Pure generation vs transformation

* **Pure Generation**: Provide minimal `creative_manifest` with just the format\_id, catalog assets (if the format renders catalog items), and any required seed assets. The creative agent generates output assets from scratch using `message` as guidance.
* **Transformation**: Provide complete `creative_manifest` with all existing assets. The creative agent adapts existing assets to the target format, optionally following guidance in `message`.

### Integration with other tasks

1. **build\_creative** → Generates manifest (optionally with inline preview via `include_preview`)
2. **preview\_creative** → Renders the manifest separately (see [preview\_creative](/docs/creative/task-reference/preview_creative))
3. **sync\_creatives** → Traffics the finalized manifest

Use `include_preview: true` to combine build and preview into one call. If the agent doesn't support it, the response simply omits the `preview` field — fall back to a separate `preview_creative` call. Either way, the preview content fields (`previews`, `interactive_url`, `expires_at`) are the same.

This separation allows you to:

* Build once, preview multiple times with different contexts
* Iterate on build without re-syncing
* Preview before committing to traffic

### Iterative refinement

`build_creative` supports multi-turn iteration without a mode flag. The presence and combination of fields determines the operation:

* **Generation**: `message` + minimal `creative_manifest` (empty or seed assets) + `target_format_id`
* **Transformation**: full `creative_manifest` + `message` + `target_format_id`
* **Library retrieval**: `creative_id` + `target_format_id` + optional `macro_values`
* **Refinement**: previous output as `creative_manifest` + `message` with changes

To refine, pass the previous response's `creative_manifest` back as input with a new `message`. Alternatively, update the brief asset (`assets.brief`) to change the creative direction — the brief is the buyer-owned source of truth for what the creative should be.

```json theme={null}
{
  "$schema": "/schemas/media-buy/build-creative-request.json",
  "idempotency_key": "b4c5d6e7-f8a9-4012-b345-012345678901",
  "message": "Make the headline bolder and increase the contrast on the CTA button",
  "target_format_id": {
    "agent_url": "https://creative.adcontextprotocol.org",
    "id": "display_300x250_generative"
  },
  "creative_manifest": {
    "format_id": {
      "agent_url": "https://creative.adcontextprotocol.org",
      "id": "display_300x250"
    },
    "assets": {
      "banner_image": {
        "asset_type": "image",
        "url": "https://cdn.creative-agent.com/generated/banner_12345.png",
        "width": 300,
        "height": 250
      },
      "headline": {
        "asset_type": "text",
        "content": "50% Off Winter Sale"
      },
      "clickthrough_url": {
        "asset_type": "url",
        "url": "https://mybrand.example.com/winter-sale?campaign={MEDIA_BUY_ID}"
      }
    }
  }
}
```

## Error codes

| Code                     | Description                                                                                                                                                                                                                                                                                                   |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FORMAT_NOT_SUPPORTED`   | The `target_format_id` (or `target_format_ids[N]` in a multi-format request) is not supported by this creative agent. For canonical creative-agent routing, retry with an advertised `creative.supported_formats[].capability_id`; legacy named-format IDs remain valid when the agent still advertises them. |
| `INVALID_MANIFEST`       | The `creative_manifest` is malformed or missing required assets for the target format                                                                                                                                                                                                                         |
| `CREATIVE_NOT_FOUND`     | The `creative_id` does not exist in the agent's library (or in the specified `concept_id`)                                                                                                                                                                                                                    |
| `COMPLIANCE_UNSATISFIED` | A required disclosure from the brief cannot be rendered in the target format (e.g., format does not support the required disclosure position)                                                                                                                                                                 |
