> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adcontextprotocol.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Creative libraries and concepts

> Creative libraries in AdCP let buyers browse, organize, and track approval status for creatives across ad servers and management platforms.

Creative libraries let buyers manage creatives through AdCP — browsing existing assets, uploading new ones, assigning them to campaigns, and tracking approval status. A creative library is hosted by any agent that declares `has_creative_library: true` in its capabilities: ad servers (CM360, Flashtalking), creative management platforms (Celtra), or sales agents that also expose the Creative Protocol.

## The model

A creative library is an account-scoped collection of creative resources with stable `creative_id`s, observable lifecycle state, and assignment relationships that are separate from any one media buy or package. The implementation can be a full ad-server library or a thin view over seller storage, but the protocol commitment is the same: buyers can read the creative resource through `list_creatives`, update it through `sync_creatives`, and reference it by `creative_id` across assignments while the seller retains it.

Inline package creatives from a seller that does not advertise a creative library are not library creatives. They are package-scoped creative attachments: the seller accepts and serves them as part of the media buy, but does not advertise independent library management or cross-buy reuse.

A creative library organizes assets at three levels:

| Level | AdCP equivalent | Examples |
| - | - | - |
| Account | Account (via [accounts protocol](/dist/docs/3.2.3/accounts/overview)) | Advertiser in CM360, brand workspace in Celtra |
| Concept | `concept_id` / `concept_name` | Flashtalking concept, CM360 creative group, Celtra campaign folder |
| Creative | Creative item with `creative_id`, `format_kind`, `assets` | A 300x250 display ad, a 30s video spot |

**Concepts** group related creatives across sizes and formats. A "Holiday 2026" concept might contain a 300x250 banner, a 728x90 leaderboard, and a 30s video — all expressing the same campaign idea. Use `concept_id` to filter and manage them as a group.

### Creative state and assignment state are separate

Two things the library tracks independently:

* **Creative state** — the review status of the creative itself: `processing`, `pending_review`, `approved`, `rejected`, or `archived`. Set by the creative agent's review workflow. Applies to the creative as a library asset, regardless of where it is used.
* **Assignment state** — the relationship between a creative and a package on a specific media buy. Created when the buyer assigns the creative (via `sync_creatives`, `creative_assignments`, or inline creatives on `create_media_buy`). Released when the media buy or package is rejected, canceled, or completed, or when the buyer removes the assignment.

These lifecycles are tracked independently:

* A creative in the library has **zero or more** active assignments at any time.
* Rejecting, canceling, or completing a media buy releases its assignments. It does not change the creative's review state, remove the creative from the library, or affect the creative's use in other media buys.
* When a creative's review state changes after assignments exist (e.g., a seller revokes an approval, or approves a previously rejected creative), sellers MAY continue or stop in-flight serving based on the new state. Buyers SHOULD re-fetch `approval_status` per package via [`get_media_buys`](/dist/docs/3.2.3/media-buy/task-reference/get_media_buys) after a creative-state change to detect assignment-level impact. See [creative review](/dist/docs/3.2.3/creative/sales-agent-creative-capabilities#creative-review).

Buyer agents reusing a library creative on a new media buy check **creative state** to know whether the asset is usable, and **assignment state** to know where it is currently in flight.

### Creatives outlast campaigns

A creative MUST persist in the library independently of the buys that reference it. Buy rejection, cancellation, or completion releases assignments only — the creative remains in the library at its current `status` and may be reused on subsequent buys. This holds regardless of how the creative entered the library: explicit [`sync_creatives`](/dist/docs/3.2.3/creative/task-reference/sync_creatives), library-backed inline creative on [`create_media_buy`](/dist/docs/3.2.3/media-buy/task-reference/create_media_buy), or platform-native upload exposed through AdCP. Sellers whose underlying ad server has no library object distinct from per-buy attachment satisfy this rule by exposing the buyer-synced creative through `list_creatives` for the buy's lifetime and continuing to expose its terminal state (including `archived`) after teardown — the library can be a thin view over per-buy storage; it does not need to be a separate store.

Retention beyond active assignments is seller-defined. An agent MAY archive an unassigned creative due to inactivity, post-flight expiry, or storage policy (see [creative status lifecycle](/dist/docs/3.2.3/creative/specification#creative-status-lifecycle)), MAY transition `approved` → `suspended` for recoverable dependency loss such as a published-post authorization expiring, and MAY transition `approved` → `rejected` for policy revocation, takedown, or content drift. Whenever a creative's state changes after it has been synced by a buyer, the seller MUST make the new state observable so the buyer can resync, replace, or stop relying on the asset before reuse:

* For state changes affecting an active media buy (e.g., `approved` → `suspended` or `approved` → `rejected` on a creative with live assignments), sellers MUST surface a corresponding `impairment` on the buy. See [media buy health](/dist/docs/3.2.3/media-buy/media-buys/lifecycle#health-and-dependency-impairment).
* For state changes on creatives with no active assignments (e.g., seller archives an unassigned creative for inactivity), sellers MUST reflect the new `status` on the next [`list_creatives`](/dist/docs/3.2.3/creative/task-reference/list_creatives) read — that snapshot is the conformant signal today, per the [snapshot-and-log contract](/dist/docs/3.2.3/protocol/snapshot-and-log). A push channel for account-scoped creative state changes is being defined under the [creative lifecycle webhooks RFC](https://github.com/adcontextprotocol/adcp/issues/2261); when that channel ships, sellers SHOULD additionally fire on it.

Buyers SHOULD confirm availability via [`list_creatives`](/dist/docs/3.2.3/creative/task-reference/list_creatives) before reuse on a new buy. A library creative is the bundle of inputs the buyer supplied — uploaded assets, a brief, brand and catalog pointers, or any combination. Retention applies to the bundle; whether the format's rendered outputs are individually addressable is a format-level concern and is independent of library retention.

## Connecting to a library

Establish account access before querying:

```json theme={null}
{
  "accounts": [{
    "account_id": "acct_acme_2026",
    "account_name": "Acme Corp",
    "credentials": {
      "api_key": "..."
    }
  }]
}
```

The account setup is the same whether the library is on a standalone creative agent or a sales agent. See [accounts protocol](/dist/docs/3.2.3/accounts/overview) for details.

## Browsing creatives

Use [`list_creatives`](/dist/docs/3.2.3/creative/task-reference/list_creatives) to browse the library. Filter by concept, format, status, tags, or date range:

```json theme={null}
{
  "filters": {
    "concept_ids": ["concept_holiday_2026"],
    "statuses": ["approved"],
    "format_kinds": ["image"]
  },
  "include": {
    "variables": true,
    "assignments": true
  }
}
```

Each creative in the response includes:

```json theme={null}
{
  "creative_id": "ft_88201",
  "name": "Holiday 2026 - Medium Rectangle",
  "format_kind": "image",
  "status": "approved",
  "concept_id": "concept_holiday_2026",
  "concept_name": "Holiday 2026 Campaign",
  "created_date": "2026-10-15T14:00:00Z",
  "updated_date": "2026-11-20T09:30:00Z",
  "tags": ["holiday_2026", "display"],
  "variables": [
    {
      "variable_id": "headline",
      "name": "Headline text",
      "type": "text",
      "default_value": "Holiday Sale — Up to 40% Off"
    }
  ],
  "assignments": [
    { "package_id": "pkg_premium_display", "weight": 100 }
  ]
}
```

The `status` field reflects the creative's current state in the library: `processing`, `pending_review`, `approved`, `rejected`, or `archived`. See [creative review](/dist/docs/3.2.3/creative/sales-agent-creative-capabilities#creative-review) for how status transitions work.

## Uploading creatives

Use [`sync_creatives`](/dist/docs/3.2.3/creative/task-reference/sync_creatives) to upload new creatives or update existing ones. The operation uses upsert semantics — if a `creative_id` already exists, it updates; otherwise it creates.

```json theme={null}
{
  "creatives": [
    {
      "creative_id": "acme_video_001",
      "name": "Holiday Sale 30s",
      "format_kind": "video_hosted",
      "assets": {
        "video": {
          "url": "https://cdn.acme-example.com/holiday-sale-30s.mp4",
          "width": 1920,
          "height": 1080,
          "duration_ms": 30000
        },
        "click_url": {
          "url": "https://acme-example.com/holiday-sale"
        }
      }
    }
  ]
}
```

The response tells you what happened to each creative:

```json theme={null}
{
  "creatives": [
    {
      "creative_id": "acme_video_001",
      "action": "created"
    }
  ]
}
```

After upload, the creative enters the library's review process. Check `list_creatives` to see when it transitions from `pending_review` to `approved`.

### Prepare creatives before a time-sensitive launch

For a seller that advertises `creative.has_creative_library: true`, sync a finished creative before booking the campaign. This lets the seller review the library asset while the buyer finalizes the media buy. Use the same account when syncing, checking status, and assigning the creative.

1. Call `sync_creatives` without an assignment. Give each request a fresh `idempotency_key`. If the seller advertises `creative.supports_revisions: true`, give the submitted content a `revision_id` so review and later readback can refer to the same input revision.
2. If the sync returns `status: "submitted"`, wait for task completion through `get_task_status` or the configured webhook. Then call `list_creatives` and wait for this creative's library `status` to become `approved`. A successful sync only confirms ingestion; it does not mean review has finished.
3. When the media buy and package exist, use `sync_creatives.assignment_operations` to assign the approved `creative_id` to the package. Read that creative's `approval_status` in the package's `creative_approvals[]` from `get_media_buys` before relying on delivery. Check `approval_scopes[]` when the status is `partially_approved`. Library approval does not guarantee eligibility for a particular package or placement.

For example, the initial sync can contain the full asset and no package reference:

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.2.3/creative/sync-creatives-request.json",
  "account": { "account_id": "acct_acme_2026" },
  "idempotency_key": "ba8fe15a-e451-486c-9cfb-4d0fdde22efa",
  "creatives": [{
    "creative_id": "acme_video_001",
    "revision_id": "acme_video_001_v1",
    "name": "Holiday Sale 30s",
    "format_kind": "video_hosted",
    "assets": {
      "video": {
        "asset_type": "video",
        "url": "https://cdn.acme-example.com/holiday-sale-30s.mp4",
        "width": 1920,
        "height": 1080,
        "duration_ms": 30000
      }
    }
  }]
}
```

After the package is created, a separate request assigns the already reviewed library creative:

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.2.3/creative/sync-creatives-request.json",
  "account": { "account_id": "acct_acme_2026" },
  "idempotency_key": "ce731bf6-ebaf-4ccc-bfe9-bf1a538848b6",
  "assignment_operations": [{
    "operation": "assign",
    "creative_id": "acme_video_001",
    "package_id": "pkg_premium_video"
  }]
}
```

For a recurring campaign, check the library status again and assign the same `creative_id` to each new package if it remains approved and eligible. To stage changed content, sync it under a new `revision_id` when revision support is advertised, wait for that revision's review, then assign it. If the earlier creative is still serving, use a separate `creative_id` for the candidate so the sync does not update the live library item. Do not reuse a `revision_id` for changed content. Sellers set review timing and policy; this workflow does not promise approval by the launch date or bypass package review.

### Uploading with assignments

Assign creatives to packages in the same call:

```json theme={null}
{
  "creatives": [
    {
      "creative_id": "acme_video_001",
      "name": "Holiday Sale 30s",
      "format_kind": "video_hosted",
      "assets": { "...": "..." }
    }
  ],
  "assignments": [
    {
      "creative_id": "acme_video_001",
      "package_id": "pkg_premium_video"
    }
  ]
}
```

## Generating tags from library creatives

When you need a serving tag for a creative in the library, use [`build_creative`](/dist/docs/3.2.3/creative/task-reference/build_creative) with `creative_id` instead of a manifest:

```json theme={null}
{
  "creative_id": "ft_88201",
  "concept_id": "concept_holiday_2026",
  "target_capability_id": "display_300x250"
}
```

The creative agent resolves the `creative_id` from its library and returns a manifest with the serving tag. The tag format depends on the platform:

* **Flashtalking, Celtra**: Universal tags that adapt to any environment. No placement context needed.
* **CM360**: Placement-level tags that require trafficking context. Pass `media_buy_id` and `package_id`:

```json theme={null}
{
  "creative_id": "cm360_creative_12345",
  "target_capability_id": "display_300x250",
  "media_buy_id": "buy_holiday_q4",
  "package_id": "pkg_premium_display"
}
```

See [tag generation models](/dist/docs/3.2.3/creative/implementing-creative-agents#tag-generation-models) for the full breakdown.

## Assigning creatives to campaigns

There are two paths for attaching library creatives to a media buy:

### Path 1: Creative assignments on the package

Reference library creatives by ID when creating the media buy:

```json theme={null}
{
  "packages": [{
    "product_id": "premium_display",
    "creative_assignments": [
      { "creative_id": "ft_88201", "weight": 60 },
      { "creative_id": "ft_88202", "weight": 40 }
    ]
  }]
}
```

This works when the creative is already in the agent's library (via `sync_creatives` or the platform's own upload flow).

### Path 2: Inline creatives on the package

Upload the creative directly with the media buy — no separate sync step:

```json theme={null}
{
  "packages": [{
    "product_id": "premium_display",
    "creatives": [{
      "creative_id": "acme_banner_001",
      "name": "Holiday banner",
      "format_kind": "image",
      "assets": { "...": "..." }
    }]
  }]
}
```

For sellers that also advertise `creative.has_creative_library: true`, the agent adds the creative to its library and assigns it to the package in one operation. For inline-only sellers that do not advertise a creative library, the same `creatives` payload assigns package-scoped assets without creating a reusable library entry. See [inline creative management](/dist/docs/3.2.3/creative/sales-agent-creative-capabilities) for details.

**Library-backed inline creatives follow the same library lifecycle as `sync_creatives` uploads.** When the seller advertises `creative.has_creative_library: true`, the inline form is a convenience — "sync and assign in one call" — not a separate lifecycle. Once submitted, the creative enters the library with the same review flow, retention, and identifiers it would have under `sync_creatives`. If the `create_media_buy` task resolves as `pending_manual` and the buy never activates, or if the buy is rejected or canceled, only the package assignments are released; the creatives remain in the library and may be referenced by `creative_id` in a subsequent `create_media_buy` call. This assignment-release behavior is normative on the media-buy side — see the [Media Buy State Transitions](/dist/docs/3.2.3/media-buy/specification#media-buy-state-transitions) rule.

For inline-only sellers, buyers should treat the creative body as attached to the media-buy package. The seller may retain enough package-scoped creative state to review, serve, replace, and audit the buy, but it has not advertised reusable library operations such as `list_creatives`, `sync_creatives`, or later `creative_assignments` reuse.

Creative review proceeds independently of the media buy outcome. Sellers MUST NOT skip review solely because the buy did not activate, and a buy rejection does not by itself imply rejection of the submitted creatives — a creative rejection MUST be a deliberate review decision with its own `rejection_reason`, not implicit from the containing buy's status. Sellers MAY deprioritize review of creatives that currently have no active assignments, provided review completes before any future assignment activates.

**Capability-flag scope.** `inline_creative_management: true` advertises that the sales agent accepts inline creatives on `create_media_buy` and `update_media_buy`; it does not by itself advertise a creative library. The decoupled library lifecycle applies when the seller also advertises `creative.has_creative_library: true`.

### Multi-seller distribution

When you work with multiple sellers, build creatives once and distribute them:

1. **Build** on your creative agent:

```json theme={null}
{
  "message": "Create a holiday promotion banner",
  "target_capability_ids": ["display_300x250", "display_728x90"]
}
```

2. **Sync** to each seller's library:

```json theme={null}
{
  "creatives": [
    {
      "creative_id": "holiday_2026_300x250",
      "name": "Holiday 2026 - Medium Rectangle",
      "concept_id": "concept_holiday_2026",
      "format_kind": "image",
      "assets": { }
    }
  ]
}
```

Call `sync_creatives` on each sales agent separately. Use the same `creative_id` and `concept_id` across sellers so you can correlate the same creative and campaign concept across your media buys. See [Multi-agent creative orchestration](/dist/docs/3.2.3/creative/multi-agent-orchestration) for the full pattern.

3. **Track approval** per seller — each seller reviews independently. Poll `list_creatives` on each agent to check status. A creative may be `approved` on one seller and `rejected` on another based on their policies.

## Tracking approval status

Creative approval operates at two levels:

**Library level**: The `status` field on each creative in [`list_creatives`](/dist/docs/3.2.3/creative/task-reference/list_creatives) — `processing`, `pending_review`, `approved`, `rejected`, or `archived`.

**Package level**: The `approval_status` on each creative in [`get_media_buys`](/dist/docs/3.2.3/media-buy/task-reference/get_media_buys) — `pending_review`, `approved`, or `rejected` with `rejection_reason`.

A creative can be `approved` in the library but `rejected` at the package level if it violates placement-specific policies. Poll both after syncing or submitting a media buy with new creatives.

## Dynamic creative optimization (DCO)

Creatives with dynamic content variables appear in the library with a `variables` array when you request `include: { variables: true }`. Each variable defines a slot that the ad server fills at serve time:

```json theme={null}
{
  "variables": [
    {
      "variable_id": "headline",
      "name": "Headline text",
      "type": "text",
      "default_value": "Holiday Sale — Up to 40% Off"
    },
    {
      "variable_id": "product_image",
      "name": "Product image",
      "type": "image",
      "default_value": "https://cdn.acme-example.com/hero.jpg"
    },
    {
      "variable_id": "cta_color",
      "name": "CTA button color",
      "type": "color",
      "default_value": "#FF6600"
    }
  ]
}
```

Use `has_variables: true` in `list_creatives` filters to find DCO creatives. Variable types match common platform patterns: `text`, `color`, `image`, `video`, `number`, `boolean`.

How the ad server uses these variables at serve time (data feeds, targeting rules, optimization algorithms) is outside AdCP's scope. AdCP models the variable *slots*, not the optimization logic.

## Next steps

* [Generative creative](/dist/docs/3.2.3/creative/generative-creative) — AI-powered creative generation and brief-in-media-buy workflows
* [Creative capabilities on sales agents](/dist/docs/3.2.3/creative/sales-agent-creative-capabilities) — When the seller manages both media and creative
* [Implementing creative agents](/dist/docs/3.2.3/creative/implementing-creative-agents) — Building an AdCP creative agent around your platform
* [sync\_creatives reference](/dist/docs/3.2.3/creative/task-reference/sync_creatives) — Upload API details
* [list\_creatives reference](/dist/docs/3.2.3/creative/task-reference/list_creatives) — Query API details with full filtering options


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