Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .changeset/2260-creative-retention-outlasts-campaigns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"adcontextprotocol": minor
---

Creative retention contract (#2260): creatives outlast campaigns, with mandatory state-change signalling.

Resolves the 3.0 ambiguity in `docs/creative/creative-libraries.mdx` ("Retention of unassigned creatives is seller-defined") without mandating a numeric retention floor that no industry platform publishes uniformly (GAM is indefinite; Meta ~37 months; FreeWheel 25 months; most others publish nothing). The protocol surface buyers actually need is observability of state changes, not a fixed number.

**Library lifecycle is independent of buy lifecycle.** A creative MUST persist in the library regardless of the status of the buys that referenced it. Buy rejection, cancellation, or completion releases assignments only. This holds for `sync_creatives`, inline creatives on `create_media_buy`, and platform-native uploads — no carve-out by submission path, and no carve-out by creative composition (assets, brief, brand+catalog pointers, or any combination).

**State changes are observable.** When a seller archives an unassigned creative, expires it for inactivity, or revokes a previously-approved creative, the seller MUST make the new state observable. For creatives with active assignments the signal is an `impairment` on the buy (existing mechanism from the dependency-impact cluster). For creatives with no active assignments the conformant signal today is the `status` value visible on the next `list_creatives` read — consistent with the [snapshot-and-log contract](docs/protocol/snapshot-and-log.mdx) which already names `list_creatives` as the reliable signal for resource-state changes outside an active buy. A push channel for account-scoped creative state changes is being defined under #2261; once that channel ships, sellers SHOULD additionally fire on it.

**Library can be a view, not a separate store.** Sellers whose underlying ad server has no library object distinct from per-buy attachment (some CTV/podcast stacks) satisfy "creatives outlast campaigns" by exposing the buyer-synced creative through `list_creatives` for the buy's lifetime and continuing to expose its terminal state after teardown.

**`creative/specification.mdx` state machine** updated to add an `approved → archived` (seller-initiated) edge, scoped to creatives without active package assignments. Sellers MUST NOT seller-archive a creative with active assignments — the existing `approved → rejected` (revocation) path with an `impairment` on the affected buy is the only conformant route when active serving is involved. The state-machine diagram and rule list both reflect the new edge.

**`creative-status.json` `archived` enumDescription** widened to acknowledge that archive can be buyer- or seller-initiated, to constrain seller-initiated archive to creatives without active assignments, and to pin the conformant signal to `list_creatives` until the push channel ships. No new enum values; no new fields. Additive description-only change.

Variant addressability — whether a format's rendered outputs (PMax-style fan-out, `responsive_creative`, `agent_placement`) carry per-variant IDs — is a format-level concern, handled in RFC #3305 / #3307, not a library-retention concern.

Closes #2260. Refs #2261 (webhook mechanics), #2254 (parent media-buy lifecycle issue, already closed), #3305 / #3307 (format-level variant addressability).
13 changes: 12 additions & 1 deletion docs/creative/creative-libraries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,18 @@ These lifecycles are tracked independently:
- 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`](/docs/media-buy/task-reference/get_media_buys) after a creative-state change to detect assignment-level impact. See [creative review](/docs/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. Retention of unassigned creatives is seller-defined in 3.0 — buyers should not assume indefinite persistence and should confirm availability via [`list_creatives`](/docs/creative/task-reference/list_creatives) before reuse on a new buy.
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`](/docs/creative/task-reference/sync_creatives), inline creative on [`create_media_buy`](/docs/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](/docs/creative/specification#creative-status-lifecycle)), 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` → `rejected` on a creative with live assignments), sellers MUST surface a corresponding `impairment` on the buy. See [media buy health](/docs/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`](/docs/creative/task-reference/list_creatives) read — that snapshot is the conformant signal today, per the [snapshot-and-log contract](/docs/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`](/docs/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

Expand Down
9 changes: 5 additions & 4 deletions docs/creative/specification.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,13 @@ Sales agents MUST translate universal macros to their ad server's native syntax.

**Schema**: [`enums/creative-status.json`](https://adcontextprotocol.org/schemas/v3/enums/creative-status.json)

Creatives in a library progress through a defined set of states. `archived` is the only buyer-initiated state change; all others are seller-initiated (processing, review, approval/rejection).
Creatives in a library progress through a defined set of states. Most transitions are seller-initiated (processing, review, approval/rejection). `archived` is reached either by buyer cleanup or by seller-side lifecycle policy on creatives without active assignments — see the rules below.

```
sync_creatives ──▶ processing ──▶ pending_review ──▶ approved
│ │
│ (processing failure) │ (buyer archives)
▼ ▼
│ (processing failure) │ (archived: buyer cleanup
▼ ▼ or seller policy)
rejected ◀── (policy failure) ── pending_review
│ archived
│ (buyer fixes + resubmits │
Expand All @@ -179,7 +179,8 @@ sync_creatives ──▶ processing ──▶ pending_review ──▶ approved
- `processing` → `rejected`: automatic when processing fails (corrupt file, unsupported codec, constraint violation)
- `pending_review` → `approved`: seller approves after content policy review
- `pending_review` → `rejected`: seller rejects with `rejection_reason`
- `approved` → `archived`: buyer-initiated via `sync_creatives`
- `approved` → `archived` (buyer-initiated): buyer issues archive via `sync_creatives`
- `approved` → `archived` (seller-initiated): seller archives an unassigned creative for inactivity, post-flight expiry, or storage policy. Sellers MUST NOT seller-archive a creative that has active package assignments — the `approved` → `rejected` (revocation) path with an `impairment` on the affected buy is the only conformant route when active serving is involved. State-change observability for seller-initiated archive follows the [creative retention contract](/docs/creative/creative-libraries#creatives-outlast-campaigns) — minimally, the new `status` MUST be visible on the next `list_creatives` read.
- `archived` → `approved`: buyer-initiated via `sync_creatives` (unarchive). Sellers MAY require re-review, transitioning to `pending_review` instead.
- `rejected` → `processing`: buyer fixes the creative and resubmits via `sync_creatives`. The creative re-enters the full processing and review pipeline.
- `approved` → `pending_review`: seller-initiated re-review (e.g., policy change). Sellers SHOULD notify the buyer when a previously approved creative is pulled back for re-review.
Expand Down
2 changes: 1 addition & 1 deletion static/schemas/source/enums/creative-status.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
"pending_review": "Creative has passed processing and is awaiting platform content policy review. Transitions to approved or rejected after review.",
"approved": "Creative has been approved and is eligible for delivery. May be archived by the buyer, or transitioned by the seller to 'pending_review' (re-review) or 'rejected' (post-approval revocation) when policy enforcement, takedown requests, or content drift require it. Sellers MUST surface a corresponding impairment on any active media buy that references this creative when transitioning 'approved' → 'rejected'.",
"rejected": "Creative was rejected due to content policy or technical issues. Reached either from 'pending_review' (initial review failure) or from 'approved' (post-approval revocation by the seller — policy change, takedown request, content drift). Not terminal — the buyer may fix the issue and resubmit via sync_creatives, which returns the creative to processing.",
"archived": "Creative has been archived by the buyer and is excluded from default queries. Archived creatives retain their data and may be unarchived back to approved."
"archived": "Creative has been archived and is excluded from default queries. Archive MAY be buyer-initiated (explicit cleanup) or seller-initiated (inactivity, post-flight expiry, storage policy) — seller-initiated archive is only valid when no active package assignments reference the creative. Archived creatives retain their data and may be unarchived back to approved by the buyer. When seller-initiated on a buyer-synced creative, the seller MUST reflect the new status on the next `list_creatives` read; a push channel for this signal is being defined under the creative lifecycle webhooks RFC."
}
}
Loading