diff --git a/CLAUDE.md b/CLAUDE.md index 2722f558bc..4dbb3c4c31 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -284,6 +284,37 @@ For major version changes: **Schema Validation**: All schemas must follow this convention. Tests will fail if format fields don't match the expected naming pattern. +### Format ID Structure + +**CRITICAL**: Format IDs are structured objects, not strings, to avoid parsing issues and provide clear namespacing. + +**Structured Format ID**: +```json +{ + "agent_url": "https://creatives.adcontextprotocol.org", + "id": "display_300x250" +} +``` + +**When to use structured format IDs**: +- **Request parameters** that accept a format ID (e.g., `target_format_id` in `build_creative`) +- **Creative asset objects** that specify which format they conform to +- Anywhere a format is being **referenced for action** + +**When to use string format IDs**: +- **Product responses** listing supported formats (for compactness) +- **Format definition objects** themselves (they have separate `format_id` and `agent_url` fields) +- Lists where verbosity is a concern + +**Schema reference**: +```json +{ + "target_format_id": { + "$ref": "/schemas/v1/core/format-id.json" + } +} +``` + ## Common Tasks ### Before Making Changes diff --git a/docs/creative/formats.md b/docs/creative/formats.md index e9446ef135..05b6ba63ad 100644 --- a/docs/creative/formats.md +++ b/docs/creative/formats.md @@ -82,6 +82,37 @@ The creative agent at that URL is the definitive source for: Buyers query the agent_url for full format details, validation, and preview capabilities. +## Referencing Formats + +When referencing a format in API requests (e.g., in `sync_creatives`, `build_creative`), use a structured format ID object that combines the agent URL and format identifier: + +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +**Why structured objects?** +- **No parsing needed**: Components are explicit +- **Unambiguous**: Clear separation of namespace and identifier +- **Extensible**: Can add version or other metadata later +- **Validation-friendly**: Easy to validate with JSON Schema + +**When formats are referenced as strings:** +In some contexts like product responses, format IDs appear as simple strings for compactness: + +```json +{ + "product_id": "ctv_premium", + "format_ids": ["video_30s_hosted", "video_15s_hosted"] +} +``` + +These strings are shorthand references. To get the full format specification, use `list_creative_formats` which returns the complete format objects with `agent_url` and all details. + ## Format Structure Formats are JSON objects with the following key fields: diff --git a/docs/creative/generative-creative.md b/docs/creative/generative-creative.md index 211c9a6520..cbc387bf62 100644 --- a/docs/creative/generative-creative.md +++ b/docs/creative/generative-creative.md @@ -12,7 +12,7 @@ The Creative Protocol provides AI-powered creative generation: - **`preview_creative`**: Generate previews of creative manifests - **`list_creative_formats`**: Discover supported creative formats -Assets are provided via [Brand Card](../reference/brand-card) - no separate asset library management needed. +Assets are provided via [Brand Manifest](../reference/brand-manifest) - no separate asset library management needed. ## Quick Start: Generate Your First Creative @@ -81,7 +81,7 @@ Use the `context_id` to make improvements: ## Common Patterns -### Using Brand Cards +### Using Brand Manifests Provide brand context for better creative generation: @@ -89,7 +89,7 @@ Provide brand context for better creative generation: { "message": "Create a display ad for our coffee shop promotion", "format_id": "display_300x250", - "brand_card": { + "brand_manifest": { "url": "https://mycoffeeshop.com", "name": "Brew & Co", "colors": { @@ -102,19 +102,19 @@ Provide brand context for better creative generation: } ``` -**Minimal Brand Card**: Start with just a URL for low-friction creative generation: +**Minimal Brand Manifest**: Start with just a URL for low-friction creative generation: ```json { "message": "Create a coffee shop ad", "format_id": "display_native", - "brand_card": { + "brand_manifest": { "url": "https://mycoffeeshop.com" } } ``` -See [Brand Card Reference](../reference/brand-card) for comprehensive examples. +See [Brand Manifest Reference](../reference/brand-manifest) for comprehensive examples. ### Using Your Own Assets @@ -124,7 +124,7 @@ Provide existing assets to incorporate into the creative: { "message": "Create a display ad featuring our signature latte", "format_id": "display_300x250", - "brand_card": { + "brand_manifest": { "url": "https://mycoffeeshop.com" }, "assets": [ @@ -191,12 +191,12 @@ If you get a format error, the publisher may not support that format. Try: ### Creative Quality Issues To improve creative output: 1. Be more specific in your message: "Create a minimalist coffee ad with earth tones" -2. Provide comprehensive brand card with assets and guidelines +2. Provide comprehensive brand manifest with assets and guidelines 3. Use the conversational refinement feature to iterate (via `context_id`) ### Asset Management -Assets are provided via [Brand Card](../reference/brand-card): -1. Include assets in brand card with descriptive tags +Assets are provided via [Brand Manifest](../reference/brand-manifest): +1. Include assets in brand manifest with descriptive tags 2. Use `asset_filters` in requests to select specific assets 3. Reference product catalogs for large inventories diff --git a/docs/creative/task-reference/build_creative.md b/docs/creative/task-reference/build_creative.md index 5706e77cb3..79b9d9029c 100644 --- a/docs/creative/task-reference/build_creative.md +++ b/docs/creative/task-reference/build_creative.md @@ -7,43 +7,18 @@ sidebar_position: 13 Build creative content for a specific format using a creative agent that can generate either a creative manifest (static mode) or executable code (dynamic mode). This tool supports conversational refinement through a series of messages. -## Format Lookup - -Creative agents need to understand format requirements to generate appropriate creatives. The format lookup process works as follows: - -1. **Standard AdCP Formats**: If `format_source` is omitted or null, the `format_id` refers to a standard AdCP format (e.g., "display_native", "video_standard_30s") - -2. **Publisher-Specific Formats**: If `format_source` is provided, the creative agent calls `list_creative_formats` on that sales agent URL to discover the format definition - -3. **Format Discovery**: Sales agents should make `list_creative_formats` accessible without authentication for creative agents to discover format requirements - -### Format Source Examples - -```json -// Standard AdCP format -{ - "format_id": "display_native" - // format_source omitted = standard format -} - -// Publisher-specific format -{ - "format_id": "premium_video_30s", - "format_source": "https://publisher.com/.well-known/adcp/sales" -} -``` +For information about format IDs and how to reference formats, see [Creative Formats - Referencing Formats](../formats.md#referencing-formats). ## Request Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `message` | string | Yes | The request message (initial brief or refinement instructions) | -| `format_source` | string | No | Source URL for format lookup (sales agent URL). If null/omitted, assumes standard AdCP format | -| `format_id` | string | Yes | Format identifier to look up from the format source | +| `source_format_id` | object | No | Format ID of existing creative to transform (optional - omit when creating from scratch). Object with `agent_url` and `id` fields. | +| `target_format_id` | object | Yes | Format ID to generate. Object with `agent_url` and `id` fields. The format definition specifies whether output is manifest-based or code-based. | | `context_id` | string | No | Session context from previous message for continuity | -| `brand_card` | BrandCard | No | Brand information manifest for creative generation context. See [Brand Card](../../reference/brand-card) for details. Replaces legacy `brand_guidelines`. | +| `brand_manifest` | BrandManifest | No | Brand information manifest containing all assets, themes, and information necessary to ensure creatives are aligned with the brand's goals and that the publisher is comfortable with what's being advertised. See [Brand Manifest](../../reference/brand-manifest) for details. | | `assets` | array | No | References to asset libraries and specific assets | -| `output_mode` | string | No | `"manifest"` for creative manifest or `"code"` for executable (default: `"manifest"`) | | `preview_options` | object | No | Options for generating preview | | `finalize` | boolean | No | Set to true to finalize the creative (default: false) | @@ -105,11 +80,16 @@ Creative agents need to understand format requirements to generate appropriate c ### Creative Output Formats -#### Manifest Mode (Static Creative) +The format definition determines whether the output is a creative manifest (asset-based) or executable code (dynamic). Both types are shown below. + +#### Creative Manifest (Asset-Based) ```json { "type": "creative_manifest", - "format_id": "display_native", + "target_format_id": { + "agent_url": "https://creatives.adcontextprotocol.org", + "id": "display_native" + }, "assets": { "headline": "Premium Dog Nutrition", "description": "Veterinarian recommended formula with real salmon", @@ -136,11 +116,14 @@ Creative agents need to understand format requirements to generate appropriate c } ``` -#### Code Mode (Dynamic Creative) +#### Creative Code (Dynamic) ```json { "type": "creative_code", - "format_id": "html5", + "target_format_id": { + "agent_url": "https://creatives.adcontextprotocol.org", + "id": "html5" + }, "code": "
\n \n
", "dependencies": { "context_required": ["weather", "time"], @@ -175,15 +158,17 @@ Creative agents need to understand format requirements to generate appropriate c ## Examples -### Example 1: Building a Native Ad with Brand Card (Manifest Mode) +### Example 1: Building a Native Ad with Brand Manifest -#### Initial Request with Brand Card +#### Initial Request with Brand Manifest ```json { "message": "Create a native ad for Yahoo promoting Purina Pro Plan. Focus on the veterinarian recommendation and that real salmon is the #1 ingredient.", - "format_id": "display_native", - "output_mode": "manifest", - "brand_card": { + "target_format_id": { + "agent_url": "https://creatives.adcontextprotocol.org", + "id": "display_native" + }, + "brand_manifest": { "url": "https://purina.com", "name": "Purina Pro Plan", "logos": [ @@ -210,13 +195,15 @@ Creative agents need to understand format requirements to generate appropriate c } ``` -#### Simple Request (Minimal Brand Card) +#### Simple Request (Minimal Brand Manifest) ```json { "message": "Create a native ad for Yahoo promoting Purina Pro Plan. Focus on the veterinarian recommendation and that real salmon is the #1 ingredient. Use an informative and trustworthy tone with 'Learn More' as the CTA.", - "format_id": "display_native", - "output_mode": "manifest", - "brand_card": { + "target_format_id": { + "agent_url": "https://creatives.adcontextprotocol.org", + "id": "display_native" + }, + "brand_manifest": { "url": "https://purina.com" }, "assets": [ @@ -236,7 +223,10 @@ Creative agents need to understand format requirements to generate appropriate c "status": "draft", "creative_output": { "type": "creative_manifest", - "format_id": "display_native", + "target_format_id": { + "agent_url": "https://creatives.adcontextprotocol.org", + "id": "display_native" + }, "assets": { "headline": "Veterinarian Recommended Nutrition", "description": "Pro Plan with real salmon as the #1 ingredient provides complete nutrition for your dog's sensitive skin and stomach.", @@ -307,7 +297,10 @@ Creative agents need to understand format requirements to generate appropriate c "status": "ready", "creative_output": { "type": "creative_manifest", - "format_id": "display_native", + "target_format_id": { + "agent_url": "https://creatives.adcontextprotocol.org", + "id": "display_native" + }, "assets": { "headline": "Veterinarian Recommended Grain-Free Nutrition", "description": "Pro Plan's grain-free formula with real salmon as the #1 ingredient provides complete nutrition without grains that can upset sensitive stomachs.", @@ -339,7 +332,10 @@ Creative agents need to understand format requirements to generate appropriate c "status": "ready", "creative_output": { "type": "creative_manifest", - "format_id": "display_native", + "target_format_id": { + "agent_url": "https://creatives.adcontextprotocol.org", + "id": "display_native" + }, "assets": { "headline": "Veterinarian Recommended Grain-Free Nutrition", "description": "Pro Plan's grain-free formula with real salmon as the #1 ingredient provides complete nutrition without grains that can upset sensitive stomachs.", @@ -359,14 +355,16 @@ Creative agents need to understand format requirements to generate appropriate c } ``` -### Example 2: Building Dynamic Video Creative (Code Mode) +### Example 2: Building Dynamic Video Creative #### Initial Conversation ```json { "message": "I need a dynamic 30-second video for Purina that adapts based on viewer context. It should be upbeat and personalized, focusing on premium nutrition tailored for each dog's needs. The CTA should be 'Find Your Formula'.", - "format_id": "video_standard_30s", - "output_mode": "code", + "target_format_id": { + "agent_url": "https://creatives.adcontextprotocol.org", + "id": "video_standard_30s" + }, "assets": [ { "library_id": "purina_video_library", @@ -431,9 +429,11 @@ Creative agents need to understand format requirements to generate appropriate c ```json { "message": "Create a short-form video ad featuring user-generated content style. Keep it authentic and fun, focusing on real pet parents and their transformation stories. Use 'See Their Story' as the CTA.", - "format_id": "custom_short_form_video", + "target_format_id": { + "agent_url": "https://publisher.com/.well-known/adcp/sales", + "id": "custom_short_form_video" + }, "format_source": "https://videoplatform.com/.well-known/adcp/sales", - "output_mode": "manifest" } ``` @@ -442,7 +442,10 @@ Creative agents need to understand format requirements to generate appropriate c { "context_id": "ctx-video-123", "creative": { - "format_id": "custom_short_form_video", + "target_format_id": { + "agent_url": "https://publisher.com/.well-known/adcp/sales", + "id": "custom_short_form_video" + }, "format_source": "https://videoplatform.com/.well-known/adcp/sales", "id": "custom_short_form_video", "name": "Short Form Video Ad", @@ -502,8 +505,9 @@ Creative agents need to understand format requirements to generate appropriate c ## Usage Notes -- **Manifest Mode**: Returns structured data that can be used with any ad server -- **Code Mode**: Returns executable HTML/JS that handles its own rendering +- **Creative Manifest**: Returns structured asset data that can be used with any ad server +- **Creative Code**: Returns executable HTML/JS that handles its own rendering +- **Output Type**: Determined by the format definition, not a parameter - **Previews**: Always check previews to ensure creative meets expectations - **Custom Formats**: Publishers should provide preview templates for non-standard formats - **Conversations**: Use natural language messages to guide the creative process diff --git a/docs/creative/task-reference/preview_creative.md b/docs/creative/task-reference/preview_creative.md index a9e7d330f0..d38346f1fa 100644 --- a/docs/creative/task-reference/preview_creative.md +++ b/docs/creative/task-reference/preview_creative.md @@ -60,12 +60,9 @@ To test multiple scenarios, provide an `inputs` array - you'll get one preview p | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `format_id` | string | Yes | Format identifier for rendering | -| `creative_manifest` | object | Yes | Complete creative manifest with all required assets | +| `creative_manifest` | object | Yes | Complete creative manifest with all required assets (brand_manifest should be included in the manifest for dynamic creatives) | | `inputs` | array | No | Array of input sets for generating multiple preview variants | | `template_id` | string | No | Specific template for custom format rendering | -| `brand_card` | object | No | Brand information manifest providing context for dynamic previews | -| `promoted_products` | object | No | Products/offerings being promoted - provides product context for previews | -| `asset_filters` | object | No | Filters to select specific assets from brand card (tags, asset_types, exclude_tags) | ### Creative Manifest Structure @@ -524,9 +521,9 @@ Response: Each `preview_url` returns an HTML page with an embedded video player showing the geo-specific variant. -### Example 4: Using Brand Card for Dynamic Previews +### Example 4: Dynamic Creative with Template Variables -Preview creative variants using brand context and product information: +Preview a dynamic creative that uses template variables for personalization: ```json { @@ -536,11 +533,15 @@ Preview creative variants using brand context and product information: "assets": { "headline": { "asset_type": "text", - "content": "{PRODUCT_NAME} - {TAGLINE}" + "content": "Discover {PRODUCT_NAME}" + }, + "description": { + "asset_type": "text", + "content": "{PRODUCT_DESCRIPTION}" }, "main_image": { "asset_type": "image", - "url": "{BRAND_ASSET_URL}" + "url": "https://cdn.acmecorp.com/hero.jpg" }, "cta": { "asset_type": "text", @@ -548,37 +549,20 @@ Preview creative variants using brand context and product information: } } }, - "brand_card": { - "url": "https://acmecorp.com", - "assets": [ - { - "asset_id": "hero_holiday", - "asset_type": "image", - "url": "https://cdn.acmecorp.com/holiday-hero.jpg", - "tags": ["holiday", "winter", "hero"] - } - ] - }, - "promoted_products": { - "skus": ["WIDGET-PRO-2024"] - }, - "asset_filters": { - "tags": ["holiday", "winter"] - }, "inputs": [ { - "name": "Holiday Desktop", + "name": "Product A", "macros": { - "DEVICE_TYPE": "desktop" - }, - "context_description": "Holiday shopping season, premium product showcase" + "PRODUCT_NAME": "Widget Pro 2024", + "PRODUCT_DESCRIPTION": "Premium quality you can trust" + } }, { - "name": "Holiday Mobile", + "name": "Product B", "macros": { - "DEVICE_TYPE": "mobile" - }, - "context_description": "Mobile browsing during holiday sales" + "PRODUCT_NAME": "Widget Lite", + "PRODUCT_DESCRIPTION": "Affordable excellence for everyone" + } } ] } @@ -591,33 +575,27 @@ Response: "adcp_version": "1.0.0", "previews": [ { - "preview_url": "https://creative-agent.example.com/preview/brand123/desktop", + "preview_url": "https://creative-agent.example.com/preview/dynamic123/a", "input": { - "name": "Holiday Desktop", + "name": "Product A", "macros": { - "DEVICE_TYPE": "desktop", "PRODUCT_NAME": "Widget Pro 2024", - "TAGLINE": "Premium Quality You Can Trust", - "BRAND_ASSET_URL": "https://cdn.acmecorp.com/holiday-hero.jpg" - }, - "context_description": "Holiday shopping season, premium product showcase" + "PRODUCT_DESCRIPTION": "Premium quality you can trust" + } } }, { - "preview_url": "https://creative-agent.example.com/preview/brand123/mobile", + "preview_url": "https://creative-agent.example.com/preview/dynamic123/b", "input": { - "name": "Holiday Mobile", + "name": "Product B", "macros": { - "DEVICE_TYPE": "mobile", - "PRODUCT_NAME": "Widget Pro 2024", - "TAGLINE": "Premium Quality You Can Trust", - "BRAND_ASSET_URL": "https://cdn.acmecorp.com/holiday-hero.jpg" - }, - "context_description": "Mobile browsing during holiday sales" + "PRODUCT_NAME": "Widget Lite", + "PRODUCT_DESCRIPTION": "Affordable excellence for everyone" + } } } ], - "interactive_url": "https://creative-agent.example.com/preview/brand123/interactive", + "interactive_url": "https://creative-agent.example.com/preview/dynamic123/interactive", "expires_at": "2025-02-15T18:00:00Z" } ``` diff --git a/docs/media-buy/creatives/index.md b/docs/media-buy/creatives/index.md index 3954a9c8cb..184e3c5642 100644 --- a/docs/media-buy/creatives/index.md +++ b/docs/media-buy/creatives/index.md @@ -126,7 +126,7 @@ AdCP uses a centralized creative library where assets are uploaded once and assi - Reuse creatives across multiple media buys - Track performance across all assignments -Asset management is handled through [Brand Cards](../../reference/brand-card), which provide brand-level assets with tags for discovery. +Asset management is handled through [Brand Manifests](../../reference/brand-manifest), which provide brand-level assets with tags for discovery. ## Platform Considerations @@ -170,7 +170,7 @@ Creative operations have varying response times: - **[`sync_creatives`](../task-reference/sync_creatives)** - Bulk creative management with upsert semantics - **[`list_creatives`](../task-reference/list_creatives)** - Advanced creative library querying and filtering - **[`list_creative_formats`](../task-reference/list_creative_formats)** - Understanding format requirements -- **[Brand Card](../../reference/brand-card)** - Brand identity and asset management +- **[Brand Manifest](../../reference/brand-manifest)** - Brand identity and asset management - **[Creative Formats](../../creative/formats)** - Understanding format specifications and discovery - **[Creative Channel Guides](../../creative/channels/video)** - Format examples across video, display, audio, DOOH, and carousels - **[Asset Types](../../creative/asset-types)** - Understanding asset roles and specifications \ No newline at end of file diff --git a/docs/media-buy/task-reference/create_media_buy.md b/docs/media-buy/task-reference/create_media_buy.md index 011953640a..8cab72240a 100644 --- a/docs/media-buy/task-reference/create_media_buy.md +++ b/docs/media-buy/task-reference/create_media_buy.md @@ -21,9 +21,9 @@ Create a media buy from selected packages. This task handles the complete workfl |-----------|------|----------|-------------| | `buyer_ref` | string | Yes | Buyer's reference identifier for this media buy | | `packages` | Package[] | Yes | Array of package configurations (see Package Object below) | -| `brand_card` | BrandCard | Yes | Brand information manifest serving as the namespace and identity for this media buy. Provides brand context, assets, and product catalog. Can be cached and reused across multiple requests. See [Brand Card](../../reference/brand-card) for details. | -| `promoted_products` | PromotedProducts | No | Products or offerings being promoted in this media buy. Supports SKU selection from brand card's product catalog, or inline offerings for non-commerce campaigns. | -| `promoted_offering` | string | No | **DEPRECATED**: Use `brand_card` with `promoted_products` instead. Legacy field for describing what is being promoted. | +| `brand_manifest` | BrandCard | Yes | Brand information manifest serving as the namespace and identity for this media buy. Provides brand context, assets, and product catalog. Can be cached and reused across multiple requests. See [Brand Manifest](../../reference/brand-manifest) for details. | +| `promoted_products` | PromotedProducts | No | Products or offerings being promoted in this media buy. Useful for campaign-level reporting, policy compliance, and publisher understanding of what's being advertised. Selects from brand manifest's product catalog using SKUs, tags, categories, or natural language queries. | +| `promoted_offering` | string | No | **DEPRECATED**: Use `brand_manifest` with `promoted_products` instead. Legacy field for describing what is being promoted. | | `po_number` | string | No | Purchase order number for tracking | | `start_time` | string | Yes | Campaign start time: `"asap"` to start as soon as possible, or ISO 8601 date-time for scheduled start | | `end_time` | string | Yes | Campaign end date/time in ISO 8601 format (UTC unless timezone specified) | diff --git a/docs/media-buy/task-reference/sync_creatives.md b/docs/media-buy/task-reference/sync_creatives.md index 5c11365e7c..58f2709441 100644 --- a/docs/media-buy/task-reference/sync_creatives.md +++ b/docs/media-buy/task-reference/sync_creatives.md @@ -16,9 +16,10 @@ The `sync_creatives` task provides a powerful, efficient approach to creative li - **Bulk Operations**: Process up to 100 creatives per request - **Upsert Semantics**: Automatic create-or-update behavior - **Patch Mode**: Update only specified fields while preserving others -- **Assignment Management**: Bulk assign creatives to packages in the same request +- **Assignment Management**: Bulk assign creatives to packages in the same request - **Validation Modes**: Choose between strict (fail-fast) or lenient (process-valid) validation - **Dry Run**: Preview changes before applying them +- **Generative Creatives**: Submit brand manifest and generation prompts for AI or human-created creatives - **Comprehensive Reporting**: Detailed results for each creative processed ## Request Parameters @@ -56,11 +57,18 @@ Each creative in the `creatives` array follows the [Creative Asset schema](/sche - `click_url`, `width`, `height`, `duration`, `tags` (optional) **Native Ad Templates:** -- `creative_id`, `name`, `format` (required) +- `creative_id`, `name`, `format_id` (required) - `snippet` (HTML template with variables like `[%Headline%]`) - `snippet_type: "html"` - `assets` array with sub-assets for template variables +**Generative Creatives:** +- `creative_id`, `name`, `format_id` (required - references a generative format) +- `assets` object with `promoted_offerings` and `generation_prompt` +- Publisher creates the creative (AI or human - buyer may not know the implementation method) +- Response includes `preview_url` when ready for review +- Note: Some buyers may care about creation method for brand safety or compliance reasons + ## Webhook Configuration (Task-Specific) For large bulk operations or creative approval workflows, you can provide a task-specific webhook to be notified when the sync completes: @@ -119,6 +127,41 @@ The response provides comprehensive details about the sync operation: } ``` +## Generative Creative Workflow + +For generative formats, buyers submit a creative manifest (brand information + generation instructions) rather than finished assets. The publisher then creates the creative - either through AI generation, human designers, or a hybrid approach. + +**Key Characteristics:** + +1. **Implementation Agnostic**: Buyer submits the same request whether the publisher uses AI, human designers, or both +2. **Async by Default**: Response time ranges from seconds (AI) to days (human review) +3. **Preview-First**: Publisher returns `preview_url` for buyer approval before campaign launch +4. **Simple Approval**: Set `approved: true` to finalize or `approved: false` with updated `generation_prompt` to request changes +5. **Conversational Refinement**: Use `context_id` from response to continue the conversation + +**Note on Transparency:** Some buyers may care about creation method (AI vs human) for brand safety, compliance, or quality reasons. Publishers should communicate their approach during format discovery or setup. + +**Protocol Context**: The `context_id` is managed at the protocol level (automatic in A2A, manual in MCP) and is not part of the task request parameters. See [Context Management](../../protocols/context-management.md) for details. + +**Workflow Steps:** + +``` +1. Buyer submits creative with promoted_offerings + generation_prompt +2. Publisher responds with status: "submitted" or "working" +3. Publisher creates creative (AI/human/hybrid) +4. Publisher responds with status: "completed" + preview_url +5. Buyer reviews preview: + - Approve: Re-submit with approved: true + - Refine: Re-submit with approved: false + updated generation_prompt +``` + +**Example Generative Formats:** +- `premium_bespoke_display` - Custom-designed display ad (human designer, 24-48h) +- `ai_native_responsive` - AI-generated native ad (automated, under 60s) +- `hybrid_video_30s` - AI draft + human polish (hybrid, 2-4h) + +The format definition specifies expected turnaround time, but the buyer's workflow is identical regardless of implementation. + ## Usage Examples ### Example 1: Full Sync with New Creatives @@ -213,7 +256,114 @@ Upload a native ad template with variable substitution: } ``` -### Example 4: Dry Run Preview +### Example 4: Generative Creative Submission + +Submit a creative manifest for publisher to create (AI or human): + +**Request:** +```json +{ + "creatives": [ + { + "creative_id": "holiday_hero_display", + "name": "Holiday Campaign Hero Display", + "format_id": { + "agent_url": "https://publisher.com/.well-known/adcp/sales", + "id": "premium_bespoke_display" + }, + "assets": { + "promoted_offerings": { + "asset_type": "promoted_offerings", + "url": "https://retailer.com", + "colors": { + "primary": "#C41E3A", + "secondary": "#165B33", + "accent": "#FFD700" + }, + "tone": "Warm, festive, family-oriented" + }, + "generation_prompt": { + "asset_type": "text", + "content": "Create a holiday campaign featuring our winter collection. Emphasize warmth, family togetherness, and quality. Include subtle holiday elements without being overtly religious." + } + }, + "tags": ["holiday", "q4_2024", "hero"] + } + ] +} +``` + +**Note:** The `url` field in the `promoted_offerings` asset represents the advertiser's brand or product website (e.g., `https://retailer.com`), not a reference to a cached manifest. Publishers can use this URL to gather additional brand context if needed. + +**Initial Response (Async):** +```json +{ + "status": "submitted", + "message": "Creative submitted for creation", + "context_id": "ctx_abc123", + "creatives": [ + { + "creative_id": "holiday_hero_display", + "action": "created", + "platform_id": "pub_creative_789" + } + ] +} +``` + +**Later Response (When Ready):** +```json +{ + "status": "completed", + "message": "Creative ready for review", + "context_id": "ctx_abc123", + "creatives": [ + { + "creative_id": "holiday_hero_display", + "action": "created", + "platform_id": "pub_creative_789", + "preview_url": "https://publisher.com/preview/pub_creative_789", + "expires_at": "2024-12-20T00:00:00Z" + } + ] +} +``` + +**Approval (Buyer likes it):** +```json +{ + "creatives": [ + { + "creative_id": "holiday_hero_display", + "approved": true + } + ] +} +``` + +**Or Request Changes (Buyer wants refinement):** +```json +{ + "creatives": [ + { + "creative_id": "holiday_hero_display", + "approved": false, + "assets": { + "generation_prompt": { + "asset_type": "text", + "content": "Make the colors more vibrant and emphasize the sale pricing more prominently" + } + } + } + ] +} +``` + +_Note: Conversational context is maintained automatically by the protocol layer - no explicit `context_id` parameter is needed in requests._ + +The buyer may not know if this creative was generated by AI in 20 seconds or designed by a human team over 2 days. The workflow is identical either way, though publishers should communicate their approach for buyers who care about creation method. + +### Example 5: Dry Run Preview Preview changes before applying them: @@ -232,7 +382,7 @@ Preview changes before applying them: } ``` -### Example 5: Library Replacement (Advanced) +### Example 6: Library Replacement (Advanced) Replace entire creative library with new set (use with extreme caution): diff --git a/docs/reference/brand-card.md b/docs/reference/brand-manifest.md similarity index 86% rename from docs/reference/brand-card.md rename to docs/reference/brand-manifest.md index 259406eb4e..45bf06e193 100644 --- a/docs/reference/brand-card.md +++ b/docs/reference/brand-manifest.md @@ -1,16 +1,16 @@ --- -title: Brand Card +title: Brand Manifest description: Standardized brand information manifest for creative generation and media buying -keywords: [brand card, brand manifest, creative generation, brand guidelines] +keywords: [brand manifest, brand manifest, creative generation, brand guidelines] --- -# Brand Card +# Brand Manifest -The **Brand Card** is a standardized manifest format that serves as the **namespace and identity** for brands in AdCP. It provides brand context, assets, and product catalogs that can be cached and reused across all media buys and creative generation requests. +The **Brand Manifest** is a standardized manifest format that serves as the **namespace and identity** for brands in AdCP. It provides brand context, assets, and product catalogs that can be cached and reused across all media buys and creative generation requests. ## Overview -Brand cards solve a key problem: how to efficiently provide brand context without requiring complex authorization flows or repeated data entry. By making the brand card the central identifier for all advertising activity, AdCP enables: +Brand cards solve a key problem: how to efficiently provide brand context without requiring complex authorization flows or repeated data entry. By making the brand manifest the central identifier for all advertising activity, AdCP enables: - **Natural grouping**: All media buys and creatives for a brand reference the same card - **Consistent identity**: Brand guidelines automatically travel with every request @@ -22,7 +22,7 @@ Brand cards solve a key problem: how to efficiently provide brand context withou - **Namespace**: Brand card serves as the primary identifier for all advertising activity - **Minimal Friction**: Start with just a name or URL, expand as needed -- **Cacheable**: Same brand card reused across all requests +- **Cacheable**: Same brand manifest reused across all requests - **Standardized**: Consistent format across all AdCP implementations - **Flexible**: Supports SMB to enterprise use cases - **AI-Optimized**: Structured for easy ingestion by creative agents @@ -31,7 +31,7 @@ Brand cards solve a key problem: how to efficiently provide brand context withou ### SMB / Ad Hoc Creative Generation -For small businesses or one-off campaigns, a minimal brand card provides enough context: +For small businesses or one-off campaigns, a minimal brand manifest provides enough context: ```json { @@ -43,7 +43,7 @@ Creative agents can infer brand information from the URL, pulling logos, colors, ### Enterprise / Established Brand -For established brands with defined guidelines, the brand card provides comprehensive context: +For established brands with defined guidelines, the brand manifest provides comprehensive context: ```json { @@ -125,9 +125,9 @@ Some brands don't have dedicated URLs (white-label products, local businesses, B - B2B brands without public sites - Sub-brands under parent company URLs -## Brand Card Schema +## Brand Manifest Schema -**Schema URL**: [/schemas/v1/core/brand-card.json](/schemas/v1/core/brand-card.json) +**Schema URL**: [/schemas/v1/core/brand-manifest.json](/schemas/v1/core/brand-manifest.json) ### Required Fields @@ -215,13 +215,13 @@ Some brands don't have dedicated URLs (white-label products, local businesses, B ### create_media_buy -Include brand card in media buy creation to provide context for creative generation: +Include brand manifest in media buy creation to provide context for creative generation: ```json { "buyer_ref": "campaign_2024_q1", "promoted_offering": "ACME Pro Widget", - "brand_card": { + "brand_manifest": { "url": "https://acmecorp.com", "name": "ACME Corporation", "tone": "professional and innovative" @@ -233,13 +233,13 @@ Include brand card in media buy creation to provide context for creative generat ### build_creative -Use brand card to inform creative generation: +Use brand manifest to inform creative generation: ```json { "message": "Create a native ad highlighting our new product launch", "format_id": "display_native", - "brand_card": { + "brand_manifest": { "url": "https://acmecorp.com", "logos": [ { @@ -291,12 +291,12 @@ Tags help creative agents select appropriate logo variants: } ``` -### 3. Cache and Reuse Brand Cards +### 3. Cache and Reuse Brand Manifests Brand cards are designed to be cached: ```javascript -// Cache brand card once +// Cache brand manifest once const brandCard = { url: "https://acmecorp.com", colors: {...}, @@ -304,9 +304,9 @@ const brandCard = { }; // Reuse across requests -await createMediaBuy({ brand_card: brandCard, ... }); -await buildCreative({ brand_card: brandCard, ... }); -await buildCreative({ brand_card: brandCard, ... }); // Same card, different creative +await createMediaBuy({ brand_manifest: brandCard, ... }); +await buildCreative({ brand_manifest: brandCard, ... }); +await buildCreative({ brand_manifest: brandCard, ... }); // Same card, different creative ``` ### 4. Product Feeds for Multi-SKU @@ -369,10 +369,10 @@ For implementations using the legacy `brand_guidelines` field in `build_creative } ``` -**After (Brand Card)**: +**After (Brand Manifest)**: ```json { - "brand_card": { + "brand_manifest": { "url": "https://brand.com", "colors": { "primary": "#FF6B35", diff --git a/static/schemas/v1/core/asset-type.json b/static/schemas/v1/core/asset-type.json index 567f9d68e0..608429c877 100644 --- a/static/schemas/v1/core/asset-type.json +++ b/static/schemas/v1/core/asset-type.json @@ -11,7 +11,7 @@ }, "type": { "type": "string", - "enum": ["image", "video", "audio", "text", "html", "css", "javascript"], + "enum": ["image", "video", "audio", "text", "html", "css", "javascript", "promoted_offerings", "url"], "description": "Type of asset" }, "required": { diff --git a/static/schemas/v1/core/assets/audio-asset.json b/static/schemas/v1/core/assets/audio-asset.json new file mode 100644 index 0000000000..f96442632b --- /dev/null +++ b/static/schemas/v1/core/assets/audio-asset.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/assets/audio-asset.json", + "title": "Audio Asset", + "description": "Audio asset with URL and specifications", + "type": "object", + "properties": { + "asset_type": { + "type": "string", + "const": "audio" + }, + "url": { + "type": "string", + "format": "uri", + "description": "URL to the audio asset" + }, + "duration_ms": { + "type": "integer", + "description": "Audio duration in milliseconds", + "minimum": 0 + }, + "format": { + "type": "string", + "description": "Audio file format (mp3, wav, aac, etc.)" + }, + "bitrate_kbps": { + "type": "integer", + "description": "Audio bitrate in kilobits per second", + "minimum": 1 + } + }, + "required": ["asset_type", "url"], + "additionalProperties": false +} diff --git a/static/schemas/v1/core/assets/css-asset.json b/static/schemas/v1/core/assets/css-asset.json new file mode 100644 index 0000000000..fe9558be09 --- /dev/null +++ b/static/schemas/v1/core/assets/css-asset.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/assets/css-asset.json", + "title": "CSS Asset", + "description": "CSS stylesheet asset", + "type": "object", + "properties": { + "asset_type": { + "type": "string", + "const": "css" + }, + "content": { + "type": "string", + "description": "CSS content" + }, + "media": { + "type": "string", + "description": "CSS media query context (e.g., 'screen', 'print')" + } + }, + "required": ["asset_type", "content"], + "additionalProperties": false +} diff --git a/static/schemas/v1/core/assets/html-asset.json b/static/schemas/v1/core/assets/html-asset.json new file mode 100644 index 0000000000..1ee7059f87 --- /dev/null +++ b/static/schemas/v1/core/assets/html-asset.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/assets/html-asset.json", + "title": "HTML Asset", + "description": "HTML content asset", + "type": "object", + "properties": { + "asset_type": { + "type": "string", + "const": "html" + }, + "content": { + "type": "string", + "description": "HTML content" + }, + "version": { + "type": "string", + "description": "HTML version (e.g., 'HTML5')" + } + }, + "required": ["asset_type", "content"], + "additionalProperties": false +} diff --git a/static/schemas/v1/core/assets/image-asset.json b/static/schemas/v1/core/assets/image-asset.json new file mode 100644 index 0000000000..2477c5c133 --- /dev/null +++ b/static/schemas/v1/core/assets/image-asset.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/assets/image-asset.json", + "title": "Image Asset", + "description": "Image asset with URL and dimensions", + "type": "object", + "properties": { + "asset_type": { + "type": "string", + "const": "image" + }, + "url": { + "type": "string", + "format": "uri", + "description": "URL to the image asset" + }, + "width": { + "type": "integer", + "description": "Image width in pixels", + "minimum": 1 + }, + "height": { + "type": "integer", + "description": "Image height in pixels", + "minimum": 1 + }, + "format": { + "type": "string", + "description": "Image file format (jpg, png, gif, webp, etc.)" + }, + "alt_text": { + "type": "string", + "description": "Alternative text for accessibility" + } + }, + "required": ["asset_type", "url"], + "additionalProperties": false +} diff --git a/static/schemas/v1/core/assets/javascript-asset.json b/static/schemas/v1/core/assets/javascript-asset.json new file mode 100644 index 0000000000..dfb9c12ae5 --- /dev/null +++ b/static/schemas/v1/core/assets/javascript-asset.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/assets/javascript-asset.json", + "title": "JavaScript Asset", + "description": "JavaScript code asset", + "type": "object", + "properties": { + "asset_type": { + "type": "string", + "const": "javascript" + }, + "content": { + "type": "string", + "description": "JavaScript content" + }, + "module_type": { + "type": "string", + "enum": ["esm", "commonjs", "script"], + "description": "JavaScript module type" + } + }, + "required": ["asset_type", "content"], + "additionalProperties": false +} diff --git a/static/schemas/v1/core/assets/promoted-offerings-asset.json b/static/schemas/v1/core/assets/promoted-offerings-asset.json new file mode 100644 index 0000000000..d760873793 --- /dev/null +++ b/static/schemas/v1/core/assets/promoted-offerings-asset.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/assets/promoted-offerings-asset.json", + "title": "Promoted Offerings Asset", + "description": "Reference to promoted offerings specification", + "type": "object", + "properties": { + "asset_type": { + "type": "string", + "const": "promoted_offerings" + }, + "url": { + "type": "string", + "format": "uri", + "description": "URL of the advertiser's brand or offering (e.g., https://retailer.com)" + }, + "colors": { + "type": "object", + "description": "Brand colors", + "properties": { + "primary": {"type": "string"}, + "secondary": {"type": "string"}, + "accent": {"type": "string"} + } + }, + "fonts": { + "type": "array", + "description": "Brand fonts", + "items": {"type": "string"} + }, + "tone": { + "type": "string", + "description": "Brand tone/voice" + } + }, + "required": ["asset_type"], + "additionalProperties": false +} diff --git a/static/schemas/v1/core/assets/text-asset.json b/static/schemas/v1/core/assets/text-asset.json new file mode 100644 index 0000000000..b519ee4b4f --- /dev/null +++ b/static/schemas/v1/core/assets/text-asset.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/assets/text-asset.json", + "title": "Text Asset", + "description": "Text content asset", + "type": "object", + "properties": { + "asset_type": { + "type": "string", + "const": "text" + }, + "content": { + "type": "string", + "description": "Text content" + }, + "max_length": { + "type": "integer", + "description": "Maximum character length constraint", + "minimum": 1 + }, + "language": { + "type": "string", + "description": "Language code (e.g., 'en', 'es', 'fr')" + } + }, + "required": ["asset_type", "content"], + "additionalProperties": false +} diff --git a/static/schemas/v1/core/assets/url-asset.json b/static/schemas/v1/core/assets/url-asset.json new file mode 100644 index 0000000000..83a812df78 --- /dev/null +++ b/static/schemas/v1/core/assets/url-asset.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/assets/url-asset.json", + "title": "URL Asset", + "description": "URL reference asset", + "type": "object", + "properties": { + "asset_type": { + "type": "string", + "const": "url" + }, + "url": { + "type": "string", + "format": "uri", + "description": "URL reference" + }, + "description": { + "type": "string", + "description": "Description of what this URL points to" + } + }, + "required": ["asset_type", "url"], + "additionalProperties": false +} diff --git a/static/schemas/v1/core/assets/video-asset.json b/static/schemas/v1/core/assets/video-asset.json new file mode 100644 index 0000000000..6c24766e22 --- /dev/null +++ b/static/schemas/v1/core/assets/video-asset.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/assets/video-asset.json", + "title": "Video Asset", + "description": "Video asset with URL and specifications", + "type": "object", + "properties": { + "asset_type": { + "type": "string", + "const": "video" + }, + "url": { + "type": "string", + "format": "uri", + "description": "URL to the video asset" + }, + "width": { + "type": "integer", + "description": "Video width in pixels", + "minimum": 1 + }, + "height": { + "type": "integer", + "description": "Video height in pixels", + "minimum": 1 + }, + "duration_ms": { + "type": "integer", + "description": "Video duration in milliseconds", + "minimum": 0 + }, + "format": { + "type": "string", + "description": "Video file format (mp4, webm, mov, etc.)" + }, + "bitrate_kbps": { + "type": "integer", + "description": "Video bitrate in kilobits per second", + "minimum": 1 + } + }, + "required": ["asset_type", "url"], + "additionalProperties": false +} diff --git a/static/schemas/v1/core/brand-card.json b/static/schemas/v1/core/brand-manifest.json similarity index 93% rename from static/schemas/v1/core/brand-card.json rename to static/schemas/v1/core/brand-manifest.json index e16eac69e5..b325506bce 100644 --- a/static/schemas/v1/core/brand-card.json +++ b/static/schemas/v1/core/brand-manifest.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "/schemas/v1/core/brand-card.json", - "title": "Brand Card", + "$id": "/schemas/v1/core/brand-manifest.json", + "title": "Brand Manifest", "description": "Standardized brand information manifest for creative generation and media buying. Enables low-friction creative workflows by providing brand context that can be easily cached and shared across requests.", "type": "object", "properties": { @@ -259,12 +259,12 @@ "created_date": { "type": "string", "format": "date-time", - "description": "When this brand card was created" + "description": "When this brand manifest was created" }, "updated_date": { "type": "string", "format": "date-time", - "description": "When this brand card was last updated" + "description": "When this brand manifest was last updated" }, "version": { "type": "string", @@ -284,23 +284,29 @@ "additionalProperties": false, "examples": [ { - "url": "https://bobsfunburgers.com", - "name": "Bob's Fun Burgers", - "description": "Example with both URL and name" + "description": "Example with both URL and name", + "data": { + "url": "https://bobsfunburgers.com", + "name": "Bob's Fun Burgers" + } }, { - "name": "Great Value", "description": "Example: white-label brand without dedicated URL", - "colors": { - "primary": "#0071CE", - "secondary": "#FFC220" - }, - "tone": "affordable and trustworthy" + "data": { + "name": "Great Value", + "colors": { + "primary": "#0071CE", + "secondary": "#FFC220" + }, + "tone": "affordable and trustworthy" + } }, { - "url": "https://acmecorp.com", - "name": "ACME Corporation", - "logos": [ + "description": "Full brand manifest with all fields", + "data": { + "url": "https://acmecorp.com", + "name": "ACME Corporation", + "logos": [ { "url": "https://cdn.acmecorp.com/logo-square-dark.png", "tags": ["dark", "square"], @@ -366,6 +372,7 @@ ], "industry": "technology", "target_audience": "business decision-makers aged 35-55" + } } ] } diff --git a/static/schemas/v1/core/creative-asset.json b/static/schemas/v1/core/creative-asset.json index df2b828a8f..5d997b77d9 100644 --- a/static/schemas/v1/core/creative-asset.json +++ b/static/schemas/v1/core/creative-asset.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/v1/core/creative-asset.json", "title": "Creative Asset", - "description": "Creative asset for upload to library - supports both hosted assets and third-party snippets", + "description": "Creative asset for upload to library - supports static assets, generative formats, and third-party snippets", "type": "object", "properties": { "creative_id": { @@ -13,42 +13,53 @@ "type": "string", "description": "Human-readable creative name" }, - "format": { - "type": "string", - "description": "Creative format type (e.g., video, audio, display)" - }, - "media_url": { - "type": "string", - "format": "uri", - "description": "URL of the creative file (for hosted assets)" - }, - "snippet": { - "type": "string", - "description": "Third-party tag, VAST XML, or code snippet (for third-party served assets)" - }, - "snippet_type": { - "$ref": "/schemas/v1/enums/snippet-type.json", - "description": "Type of snippet content" + "format_id": { + "$ref": "/schemas/v1/core/format-id.json", + "description": "Format identifier specifying which format this creative conforms to" }, - "click_url": { - "type": "string", - "format": "uri", - "description": "Landing page URL for the creative" - }, - "duration": { - "type": "number", - "description": "Duration in milliseconds (for video/audio)", - "minimum": 0 - }, - "width": { - "type": "number", - "description": "Width in pixels (for video/display)", - "minimum": 0 + "assets": { + "type": "object", + "description": "Assets required by the format, keyed by asset_role", + "patternProperties": { + "^[a-zA-Z0-9_-]+$": { + "oneOf": [ + {"$ref": "/schemas/v1/core/assets/image-asset.json"}, + {"$ref": "/schemas/v1/core/assets/video-asset.json"}, + {"$ref": "/schemas/v1/core/assets/audio-asset.json"}, + {"$ref": "/schemas/v1/core/assets/text-asset.json"}, + {"$ref": "/schemas/v1/core/assets/html-asset.json"}, + {"$ref": "/schemas/v1/core/assets/css-asset.json"}, + {"$ref": "/schemas/v1/core/assets/javascript-asset.json"}, + {"$ref": "/schemas/v1/core/assets/promoted-offerings-asset.json"}, + {"$ref": "/schemas/v1/core/assets/url-asset.json"} + ] + } + }, + "additionalProperties": false }, - "height": { - "type": "number", - "description": "Height in pixels (for video/display)", - "minimum": 0 + "inputs": { + "type": "array", + "description": "Preview contexts for generative formats - defines what scenarios to generate previews for", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Human-readable name for this preview variant" + }, + "macros": { + "type": "object", + "description": "Macro values to apply for this preview", + "additionalProperties": {"type": "string"} + }, + "context_description": { + "type": "string", + "description": "Natural language description of the context for AI-generated content" + } + }, + "required": ["name"], + "additionalProperties": false + } }, "tags": { "type": "array", @@ -57,33 +68,11 @@ "type": "string" } }, - "assets": { - "type": "array", - "description": "Sub-assets for multi-asset formats like carousels", - "items": { - "$ref": "/schemas/v1/core/sub-asset.json" - } + "approved": { + "type": "boolean", + "description": "For generative creatives: set to true to approve and finalize, false to request regeneration with updated assets/message. Omit for non-generative creatives." } }, - "required": ["creative_id", "name", "format"], - "oneOf": [ - { - "description": "Hosted asset - requires media_url", - "required": ["media_url"], - "not": { - "anyOf": [ - { "required": ["snippet"] }, - { "required": ["snippet_type"] } - ] - } - }, - { - "description": "Third-party asset - requires snippet and snippet_type", - "required": ["snippet", "snippet_type"], - "not": { - "required": ["media_url"] - } - } - ], + "required": ["creative_id", "name", "format_id", "assets"], "additionalProperties": false } \ No newline at end of file diff --git a/static/schemas/v1/core/format-id.json b/static/schemas/v1/core/format-id.json new file mode 100644 index 0000000000..5469282f37 --- /dev/null +++ b/static/schemas/v1/core/format-id.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/format-id.json", + "title": "Format ID", + "description": "Structured format identifier with agent URL and format name", + "type": "object", + "properties": { + "agent_url": { + "type": "string", + "format": "uri", + "description": "URL of the agent that defines this format (e.g., 'https://creatives.adcontextprotocol.org' for standard formats, or 'https://publisher.com/.well-known/adcp/sales' for custom formats)" + }, + "id": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$", + "description": "Format identifier within the agent's namespace (e.g., 'display_300x250', 'video_standard_30s')" + } + }, + "required": ["agent_url", "id"], + "additionalProperties": false +} diff --git a/static/schemas/v1/core/format.json b/static/schemas/v1/core/format.json index ed8c802776..14827f24b6 100644 --- a/static/schemas/v1/core/format.json +++ b/static/schemas/v1/core/format.json @@ -18,6 +18,20 @@ "type": "string", "description": "Human-readable format name" }, + "description": { + "type": "string", + "description": "Plain text explanation of what this format does and what assets it requires" + }, + "preview_image": { + "type": "string", + "format": "uri", + "description": "Optional preview image URL for format browsing/discovery UI" + }, + "example_url": { + "type": "string", + "format": "uri", + "description": "Optional URL to showcase page with examples and interactive demos of this format" + }, "type": { "type": "string", "description": "Media type of this format - determines rendering method and asset requirements", @@ -53,7 +67,7 @@ "asset_type": { "type": "string", "description": "Type of asset", - "enum": ["image", "video", "audio", "text", "html", "javascript", "url"] + "enum": ["image", "video", "audio", "text", "html", "javascript", "url", "brand_manifest"] }, "asset_role": { "type": "string", @@ -107,7 +121,7 @@ "asset_type": { "type": "string", "description": "Type of asset", - "enum": ["image", "video", "audio", "text", "html", "javascript", "url"] + "enum": ["image", "video", "audio", "text", "html", "javascript", "url", "brand_manifest"] }, "asset_role": { "type": "string", diff --git a/static/schemas/v1/core/promoted-offerings.json b/static/schemas/v1/core/promoted-offerings.json new file mode 100644 index 0000000000..c6eb8f2b36 --- /dev/null +++ b/static/schemas/v1/core/promoted-offerings.json @@ -0,0 +1,90 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/v1/core/promoted-offerings.json", + "title": "Promoted Offerings", + "description": "Complete offering specification combining brand manifest, product selectors, and asset filters. Provides all context needed for creative generation about what is being promoted.", + "type": "object", + "properties": { + "brand_manifest": { + "$ref": "/schemas/v1/core/brand-manifest.json", + "description": "Brand information manifest containing assets, themes, and guidelines" + }, + "product_selectors": { + "$ref": "/schemas/v1/core/promoted-products.json", + "description": "Selectors to choose which products/offerings from the brand manifest product catalog to promote" + }, + "offerings": { + "type": "array", + "description": "Inline offerings for campaigns without a product catalog. Each offering has a name, description, and associated assets.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Offering name (e.g., 'Winter Sale', 'New Product Launch')" + }, + "description": { + "type": "string", + "description": "Description of what's being offered" + }, + "assets": { + "type": "array", + "description": "Assets specific to this offering", + "items": { + "type": "object", + "description": "Asset definition using standard asset structure", + "additionalProperties": true + } + } + }, + "required": ["name"], + "additionalProperties": false + } + }, + "asset_selectors": { + "type": "object", + "description": "Selectors to choose specific assets from the brand manifest", + "properties": { + "tags": { + "type": "array", + "description": "Select assets with specific tags (e.g., ['holiday', 'premium'])", + "items": { + "type": "string" + } + }, + "asset_types": { + "type": "array", + "description": "Filter by asset type (e.g., ['image', 'video'])", + "items": { + "type": "string", + "enum": ["image", "video", "audio", "text", "html", "css", "javascript"] + } + }, + "exclude_tags": { + "type": "array", + "description": "Exclude assets with these tags", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "required": ["brand_manifest"], + "additionalProperties": false, + "examples": [ + { + "brand_manifest": { + "url": "https://brand.com" + }, + "product_selectors": { + "manifest_skus": ["SKU-123", "SKU-456"] + }, + "asset_selectors": { + "tags": ["holiday"], + "asset_types": ["image", "video"] + } + } + ] +} diff --git a/static/schemas/v1/core/promoted-products.json b/static/schemas/v1/core/promoted-products.json index 74ac1d3ec9..faf50397d0 100644 --- a/static/schemas/v1/core/promoted-products.json +++ b/static/schemas/v1/core/promoted-products.json @@ -2,157 +2,58 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/v1/core/promoted-products.json", "title": "Promoted Products", - "description": "Specification of products or offerings being promoted in a campaign. Supports multiple selection methods that can be combined using UNION (OR) logic: direct SKUs, tag-based filtering, category selection, natural language queries, and inline offering definitions for non-commerce campaigns. When multiple selection methods are provided, products matching ANY of the criteria are selected (logical OR, not AND).", + "description": "Specification of products or offerings being promoted in a campaign. Supports multiple selection methods from the brand manifest that can be combined using UNION (OR) logic. When multiple selection methods are provided, products matching ANY of the criteria are selected (logical OR, not AND).", "type": "object", "properties": { - "skus": { + "manifest_skus": { "type": "array", - "description": "Direct product SKU references from the brand's product catalog", + "description": "Direct product SKU references from the brand manifest product catalog", "items": { "type": "string" } }, - "tags": { + "manifest_tags": { "type": "array", - "description": "Select products by tags from the product catalog (e.g., 'organic', 'sauces', 'holiday')", + "description": "Select products by tags from the brand manifest product catalog (e.g., 'organic', 'sauces', 'holiday')", "items": { "type": "string" } }, - "category": { + "manifest_category": { "type": "string", - "description": "Select products from a specific category in the product catalog (e.g., 'beverages/soft-drinks', 'food/sauces')" + "description": "Select products from a specific category in the brand manifest product catalog (e.g., 'beverages/soft-drinks', 'food/sauces')" }, - "query": { + "manifest_query": { "type": "string", - "description": "Natural language query to select products (e.g., 'all Kraft Heinz pasta sauces', 'organic products under $20')" - }, - "offerings": { - "type": "array", - "description": "Inline offering definitions for non-commerce campaigns or campaigns without a product catalog. Use when promoting services, events, brand awareness, or other non-SKU offerings.", - "items": { - "type": "object", - "properties": { - "offering_id": { - "type": "string", - "description": "Unique identifier for this offering" - }, - "name": { - "type": "string", - "description": "Name of the offering being promoted" - }, - "description": { - "type": "string", - "description": "Description of what is being promoted" - }, - "message": { - "type": "string", - "description": "Product-specific message or context for creative generation. Provides additional context beyond the overall campaign message." - }, - "link": { - "type": "string", - "format": "uri", - "description": "Landing page or destination URL for this offering" - }, - "assets": { - "type": "array", - "description": "Campaign-specific assets for this offering (in addition to brand card assets)", - "items": { - "type": "object", - "properties": { - "asset_id": { - "type": "string" - }, - "asset_type": { - "type": "string", - "enum": ["image", "video", "audio", "text"] - }, - "url": { - "type": "string", - "format": "uri" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "width": { - "type": "integer" - }, - "height": { - "type": "integer" - }, - "duration_seconds": { - "type": "number" - }, - "format": { - "type": "string" - } - }, - "required": ["asset_id", "asset_type", "url"] - } - }, - "metadata": { - "type": "object", - "description": "Additional offering-specific metadata", - "additionalProperties": true - } - }, - "required": ["name", "description"], - "additionalProperties": false - } + "description": "Natural language query to select products from the brand manifest (e.g., 'all Kraft Heinz pasta sauces', 'organic products under $20')" } }, "additionalProperties": false, "examples": [ { - "skus": ["SKU-12345", "SKU-67890"], - "description": "Direct SKU selection for specific products" - }, - { - "tags": ["organic", "sauces"], - "category": "food/condiments", - "description": "UNION selection: products tagged 'organic' OR 'sauces' OR in 'food/condiments' category" + "description": "Direct SKU selection for specific products from brand manifest", + "data": { + "manifest_skus": ["SKU-12345", "SKU-67890"] + } }, { - "query": "all Kraft Heinz pasta sauces under $5", - "description": "Natural language product selection" + "description": "UNION selection: products tagged 'organic' OR 'sauces' OR in 'food/condiments' category from brand manifest", + "data": { + "manifest_tags": ["organic", "sauces"], + "manifest_category": "food/condiments" + } }, { - "offerings": [ - { - "offering_id": "q1_brand_awareness", - "name": "Q1 Brand Awareness Campaign", - "description": "Promote our new sustainability initiative and brand values", - "message": "Focus on environmental impact and corporate responsibility", - "link": "https://brand.com/sustainability", - "assets": [ - { - "asset_id": "sustainability_hero", - "asset_type": "image", - "url": "https://cdn.brand.com/sustainability-hero.jpg", - "tags": ["campaign", "sustainability"], - "width": 1920, - "height": 1080, - "format": "jpg" - } - ] - } - ], - "description": "Non-commerce campaign with inline offering" + "description": "Natural language product selection from brand manifest", + "data": { + "manifest_query": "all Kraft Heinz pasta sauces under $5" + } }, { - "skus": ["SKU-123"], - "offerings": [ - { - "offering_id": "holiday_promo", - "name": "Holiday Bundle Promotion", - "description": "Buy 2 get 1 free holiday special", - "message": "Limited time offer - perfect for gift giving" - } - ], - "description": "Combine catalog SKUs with promotional offering context" + "description": "Select products by tags", + "data": { + "manifest_tags": ["holiday"] + } } ] } diff --git a/static/schemas/v1/creative/preview-creative-request.json b/static/schemas/v1/creative/preview-creative-request.json index f1ddf5d970..294eea9d0b 100644 --- a/static/schemas/v1/creative/preview-creative-request.json +++ b/static/schemas/v1/creative/preview-creative-request.json @@ -49,42 +49,9 @@ "type": "string", "description": "Specific template ID for custom format rendering" }, - "brand_card": { - "$ref": "/schemas/v1/core/brand-card.json", - "description": "Brand information manifest providing context for preview generation. Used when creative manifest includes dynamic elements that need brand context." - }, - "promoted_products": { - "$ref": "/schemas/v1/core/promoted-products.json", - "description": "Products or offerings being promoted in this creative. Provides product-specific context for dynamic creative previews." - }, - "asset_filters": { - "type": "object", - "description": "Filters to select specific assets from the brand card's asset library for preview variants", - "properties": { - "tags": { - "type": "array", - "description": "Select assets matching any of these tags (e.g., ['holiday', 'winter'])", - "items": { - "type": "string" - } - }, - "asset_types": { - "type": "array", - "description": "Limit to specific asset types", - "items": { - "type": "string", - "enum": ["image", "video", "audio", "text"] - } - }, - "exclude_tags": { - "type": "array", - "description": "Exclude assets with these tags", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false + "promoted_offerings": { + "$ref": "/schemas/v1/core/promoted-offerings.json", + "description": "Complete offering specification for dynamic creative previews - includes brand manifest, product selectors, inline offerings, and asset filters" } }, "required": ["format_id", "creative_manifest"], diff --git a/static/schemas/v1/enums/snippet-type.json b/static/schemas/v1/enums/snippet-type.json index 1444762b42..70b7559589 100644 --- a/static/schemas/v1/enums/snippet-type.json +++ b/static/schemas/v1/enums/snippet-type.json @@ -21,36 +21,12 @@ "daast_url": "DAAST (Digital Audio Ad Serving Template) URL for audio advertisements" }, "examples": [ - { - "type": "vast_xml", - "description": "Inline VAST XML", - "snippet": "Sample Ad..." - }, - { - "type": "vast_url", - "description": "VAST endpoint URL", - "snippet": "https://ads.example.com/vast?campaign=12345&placement=video" - }, - { - "type": "html", - "description": "HTML display ad", - "snippet": "
\"Ad\"/
" - }, - { - "type": "javascript", - "description": "JavaScript ad tag", - "snippet": "" - }, - { - "type": "iframe", - "description": "iFrame ad tag", - "snippet": "" - }, - { - "type": "daast_url", - "description": "DAAST audio ad URL", - "snippet": "https://audio-ads.example.com/daast?campaign=audio123" - } + "vast_xml", + "vast_url", + "html", + "javascript", + "iframe", + "daast_url" ], "usage": { "vast_xml": { diff --git a/static/schemas/v1/index.json b/static/schemas/v1/index.json index 2e7da3cb7a..38d4b5231e 100644 --- a/static/schemas/v1/index.json +++ b/static/schemas/v1/index.json @@ -83,8 +83,8 @@ "$ref": "/schemas/v1/core/property.json", "description": "An advertising property that can be validated via adagents.json" }, - "brand-card": { - "$ref": "/schemas/v1/core/brand-card.json", + "brand-manifest": { + "$ref": "/schemas/v1/core/brand-manifest.json", "description": "Standardized brand information manifest for creative generation and media buying" }, "promoted-products": { diff --git a/static/schemas/v1/media-buy/build-creative-request.json b/static/schemas/v1/media-buy/build-creative-request.json index 90cd8d3c20..40053170cb 100644 --- a/static/schemas/v1/media-buy/build-creative-request.json +++ b/static/schemas/v1/media-buy/build-creative-request.json @@ -15,96 +15,22 @@ "type": "string", "description": "Natural language description of the creative brief or requirements" }, - "format_source": { - "type": "string", - "description": "Source URL for format lookup (sales agent URL) or null for standard AdCP formats", - "format": "uri" - }, - "format_id": { - "type": "string", - "description": "Format identifier to look up from the format source" + "source_format_id": { + "$ref": "/schemas/v1/core/format-id.json", + "description": "Format ID of existing creative to transform (optional - omit when creating from scratch)" }, - "context_id": { - "type": "string", - "description": "Optional context ID for iterative creative refinement" + "target_format_id": { + "$ref": "/schemas/v1/core/format-id.json", + "description": "Format ID to generate. The format definition specifies whether output is manifest-based or code-based." }, "assets": { "type": "array", - "description": "Available assets to use in creative generation", + "description": "Available assets to use in creative generation - this is where promoted_offerings should be included as an asset", "items": { "$ref": "/schemas/v1/core/creative-asset.json" } - }, - "output_mode": { - "type": "string", - "enum": ["manifest", "code"], - "default": "manifest", - "description": "Output format - 'manifest' for asset-based creatives, 'code' for executable creative code" - }, - "brand_card": { - "$ref": "/schemas/v1/core/brand-card.json", - "description": "Brand information manifest providing context for creative generation. Replaces brand_guidelines with a more comprehensive and standardized format." - }, - "promoted_products": { - "$ref": "/schemas/v1/core/promoted-products.json", - "description": "Products or offerings being promoted in this creative. Supports SKU selection from brand card's product catalog, or inline offerings for non-commerce campaigns." - }, - "asset_filters": { - "type": "object", - "description": "Filters to select specific assets from the brand card's asset library", - "properties": { - "tags": { - "type": "array", - "description": "Select assets matching any of these tags (e.g., ['holiday', 'winter'])", - "items": { - "type": "string" - } - }, - "asset_types": { - "type": "array", - "description": "Limit to specific asset types", - "items": { - "type": "string", - "enum": ["image", "video", "audio", "text"] - } - }, - "exclude_tags": { - "type": "array", - "description": "Exclude assets with these tags", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - }, - "brand_guidelines": { - "type": "object", - "description": "DEPRECATED: Use brand_card instead. Legacy brand-specific guidelines and constraints.", - "properties": { - "colors": { - "type": "array", - "items": {"type": "string"}, - "description": "Brand color palette (hex codes)" - }, - "fonts": { - "type": "array", - "items": {"type": "string"}, - "description": "Approved brand fonts" - }, - "tone": { - "type": "string", - "description": "Brand tone and voice guidelines" - }, - "restrictions": { - "type": "array", - "items": {"type": "string"}, - "description": "Content restrictions or requirements" - } - }, - "additionalProperties": false } }, - "required": ["message", "format_id"], + "required": ["message", "target_format_id"], "additionalProperties": false } \ No newline at end of file diff --git a/static/schemas/v1/media-buy/create-media-buy-request.json b/static/schemas/v1/media-buy/create-media-buy-request.json index e280869f28..1285ff1137 100644 --- a/static/schemas/v1/media-buy/create-media-buy-request.json +++ b/static/schemas/v1/media-buy/create-media-buy-request.json @@ -22,17 +22,13 @@ "$ref": "/schemas/v1/media-buy/package-request.json" } }, - "brand_card": { - "$ref": "/schemas/v1/core/brand-card.json", + "brand_manifest": { + "$ref": "/schemas/v1/core/brand-manifest.json", "description": "Brand information manifest serving as the namespace and identity for this media buy. Provides brand context, assets, and product catalog. Can be cached and reused across multiple requests." }, "promoted_offering": { "type": "string", - "description": "DEPRECATED: Use brand_card with promoted_products instead. Legacy field for describing what is being promoted." - }, - "promoted_products": { - "$ref": "/schemas/v1/core/promoted-products.json", - "description": "Products or offerings being promoted in this media buy. Supports SKU selection from brand card's product catalog, or inline offerings for non-commerce campaigns." + "description": "DEPRECATED: Use brand_manifest instead. Legacy field for describing what is being promoted." }, "po_number": { "type": "string", @@ -78,6 +74,6 @@ ] } }, - "required": ["buyer_ref", "packages", "brand_card", "start_time", "end_time", "budget"], + "required": ["buyer_ref", "packages", "brand_manifest", "start_time", "end_time", "budget"], "additionalProperties": false } \ No newline at end of file diff --git a/static/schemas/v1/media-buy/sync-creatives-request.json b/static/schemas/v1/media-buy/sync-creatives-request.json index a6bfc20dd3..5db92c3290 100644 --- a/static/schemas/v1/media-buy/sync-creatives-request.json +++ b/static/schemas/v1/media-buy/sync-creatives-request.json @@ -63,15 +63,25 @@ "additionalProperties": false, "examples": [ { - "description": "Full sync with new and updated creatives", + "description": "Full sync with hosted video creative", "data": { "creatives": [ { "creative_id": "hero_video_30s", "name": "Brand Hero Video 30s", - "format": "video_30s_vast", - "snippet": "https://vast.example.com/video/123", - "snippet_type": "vast_url", + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "video_standard_30s" + }, + "assets": { + "video": { + "asset_type": "video", + "url": "https://cdn.example.com/hero-video.mp4", + "width": 1920, + "height": 1080, + "duration_ms": 30000 + } + }, "tags": ["q1_2024", "video"] } ], @@ -81,29 +91,33 @@ } }, { - "description": "Patch update - only update click URLs", + "description": "Generative creative with approval", "data": { "creatives": [ { - "creative_id": "hero_video_30s", - "click_url": "https://example.com/new-landing" - } - ], - "patch": true - } - }, - { - "description": "Dry run to preview changes", - "data": { - "creatives": [ - { - "creative_id": "new_banner", - "name": "Test Banner", - "format": "display_300x250", - "media_url": "https://cdn.example.com/banner.jpg" + "creative_id": "holiday_hero", + "name": "Holiday Campaign Hero", + "format_id": { + "agent_url": "https://publisher.com/.well-known/adcp/sales", + "id": "premium_bespoke_display" + }, + "assets": { + "promoted_offerings": { + "asset_type": "promoted_offerings", + "url": "https://retailer.com", + "colors": { + "primary": "#C41E3A", + "secondary": "#165B33" + } + }, + "generation_prompt": { + "asset_type": "text", + "content": "Create a warm, festive holiday campaign featuring winter products" + } + }, + "tags": ["holiday", "q4_2024"] } - ], - "dry_run": true + ] } } ] diff --git a/static/schemas/v1/media-buy/sync-creatives-response.json b/static/schemas/v1/media-buy/sync-creatives-response.json index 4fd1b4a8d9..a769213d11 100644 --- a/static/schemas/v1/media-buy/sync-creatives-response.json +++ b/static/schemas/v1/media-buy/sync-creatives-response.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/v1/media-buy/sync-creatives-response.json", "title": "Sync Creatives Response", - "description": "Response from creative sync operation with detailed results and bulk operation summary", + "description": "Response from creative sync operation with results for each creative", "type": "object", "properties": { "adcp_version": { @@ -12,11 +12,11 @@ }, "message": { "type": "string", - "description": "Human-readable result message summarizing the sync operation" + "description": "Human-readable result message (e.g., 'Synced 3 creatives: 2 created, 1 updated')" }, "context_id": { "type": "string", - "description": "Context ID for tracking async operations" + "description": "Context ID for tracking async operations and conversational approval workflows" }, "status": { "$ref": "/schemas/v1/enums/task-status.json", @@ -31,47 +31,9 @@ "type": "boolean", "description": "Whether this was a dry run (no actual changes made)" }, - "summary": { - "type": "object", - "description": "High-level summary of sync operation results", - "properties": { - "total_processed": { - "type": "integer", - "description": "Total number of creatives processed", - "minimum": 0 - }, - "created": { - "type": "integer", - "description": "Number of new creatives created", - "minimum": 0 - }, - "updated": { - "type": "integer", - "description": "Number of existing creatives updated", - "minimum": 0 - }, - "unchanged": { - "type": "integer", - "description": "Number of creatives that were already up-to-date", - "minimum": 0 - }, - "failed": { - "type": "integer", - "description": "Number of creatives that failed validation or processing", - "minimum": 0 - }, - "deleted": { - "type": "integer", - "description": "Number of creatives deleted/archived (when delete_missing=true)", - "minimum": 0 - } - }, - "required": ["total_processed", "created", "updated", "unchanged", "failed"], - "additionalProperties": false - }, - "results": { + "creatives": { "type": "array", - "description": "Detailed results for each creative processed", + "description": "Results for each creative processed", "items": { "type": "object", "properties": { @@ -84,24 +46,20 @@ "enum": ["created", "updated", "unchanged", "failed", "deleted"], "description": "Action taken for this creative" }, - "status": { - "$ref": "/schemas/v1/enums/creative-status.json", - "description": "Current approval status of the creative" - }, "platform_id": { "type": "string", "description": "Platform-specific ID assigned to the creative" }, "changes": { "type": "array", - "description": "List of field names that were modified (for 'updated' action)", + "description": "Field names that were modified (only present when action='updated')", "items": { "type": "string" } }, "errors": { "type": "array", - "description": "Validation or processing errors (for 'failed' action)", + "description": "Validation or processing errors (only present when action='failed')", "items": { "type": "string" } @@ -113,123 +71,40 @@ "type": "string" } }, - "review_feedback": { + "preview_url": { "type": "string", - "description": "Feedback from platform review process" + "format": "uri", + "description": "Preview URL for generative creatives (only present for generative formats)" }, - "suggested_adaptations": { - "type": "array", - "description": "Recommended creative adaptations for better performance", - "items": { - "type": "object", - "properties": { - "adaptation_id": { - "type": "string", - "description": "Unique identifier for this adaptation" - }, - "format_id": { - "type": "string", - "description": "Target format ID for the adaptation" - }, - "name": { - "type": "string", - "description": "Suggested name for the adapted creative" - }, - "description": { - "type": "string", - "description": "What this adaptation does" - }, - "estimated_performance_lift": { - "type": "number", - "description": "Expected performance improvement (percentage)" - } - }, - "required": ["adaptation_id", "format_id", "name", "description"], - "additionalProperties": false - } - } - }, - "required": ["creative_id", "action"], - "additionalProperties": false - } - }, - "assignments_summary": { - "type": "object", - "description": "Summary of assignment operations (when assignments were included in request)", - "properties": { - "total_assignments_processed": { - "type": "integer", - "description": "Total number of creative-package assignment operations processed", - "minimum": 0 - }, - "assigned": { - "type": "integer", - "description": "Number of successful creative-package assignments", - "minimum": 0 - }, - "unassigned": { - "type": "integer", - "description": "Number of creative-package unassignments", - "minimum": 0 - }, - "failed": { - "type": "integer", - "description": "Number of assignment operations that failed", - "minimum": 0 - } - }, - "required": ["total_assignments_processed", "assigned", "unassigned", "failed"], - "additionalProperties": false - }, - "assignment_results": { - "type": "array", - "description": "Detailed assignment results (when assignments were included in request)", - "items": { - "type": "object", - "properties": { - "creative_id": { + "expires_at": { "type": "string", - "description": "Creative that was assigned/unassigned" + "format": "date-time", + "description": "ISO 8601 timestamp when preview link expires (only present when preview_url exists)" }, - "assigned_packages": { + "assigned_to": { "type": "array", - "description": "Packages successfully assigned to this creative", + "description": "Package IDs this creative was successfully assigned to (only present when assignments were requested)", "items": { "type": "string" } }, - "unassigned_packages": { - "type": "array", - "description": "Packages successfully unassigned from this creative", - "items": { - "type": "string" - } - }, - "failed_packages": { - "type": "array", - "description": "Packages that failed to assign/unassign", - "items": { - "type": "object", - "properties": { - "package_id": { - "type": "string", - "description": "Package ID that failed" - }, - "error": { - "type": "string", - "description": "Error message for the failed assignment" - } - }, - "required": ["package_id", "error"], - "additionalProperties": false - } + "assignment_errors": { + "type": "object", + "description": "Assignment errors by package ID (only present when assignment failures occurred)", + "patternProperties": { + "^[a-zA-Z0-9_-]+$": { + "type": "string", + "description": "Error message for this package assignment" + } + }, + "additionalProperties": false } }, - "required": ["creative_id"], + "required": ["creative_id", "action"], "additionalProperties": false } } }, - "required": ["adcp_version", "message", "status"], + "required": ["adcp_version", "message", "status", "creatives"], "additionalProperties": false } \ No newline at end of file diff --git a/tests/schema-validation.test.js b/tests/schema-validation.test.js index 53707951eb..adcc607958 100644 --- a/tests/schema-validation.test.js +++ b/tests/schema-validation.test.js @@ -275,19 +275,19 @@ async function runTests() { 'product.json': ['product_id', 'name', 'description', 'format_ids', 'delivery_type', 'is_fixed_price'], 'media-buy.json': ['media_buy_id', 'status', 'promoted_offering', 'total_budget', 'packages'], 'package.json': ['package_id', 'status'], - 'creative-asset.json': ['creative_id', 'name', 'format'], + 'creative-asset.json': ['creative_id', 'name', 'format_id', 'assets'], 'error.json': ['code', 'message'], 'budget.json': ['total', 'currency'] }; - + for (const [schemaPath, schema] of coreSchemas) { const filename = path.basename(schemaPath); const expectedRequired = requiredFieldChecks[filename]; - + if (expectedRequired) { const actualRequired = schema.required || []; const missing = expectedRequired.filter(field => !actualRequired.includes(field)); - + if (missing.length > 0) { return `${filename}: Missing required fields: ${missing.join(', ')}`; } @@ -296,6 +296,46 @@ async function runTests() { return true; }); + // Test 7: Validate schema examples against their schemas + await test('Schema examples validate against their own schemas', async () => { + const schemasWithExamples = schemas.filter(([_, schema]) => schema.examples && schema.examples.length > 0); + + for (const [schemaPath, schema] of schemasWithExamples) { + const filename = path.basename(schemaPath); + + // Compile the schema + const testAjv = new Ajv({ + allErrors: true, + verbose: true, + strict: false, + loadSchema: loadExternalSchema + }); + addFormats(testAjv); + + let validate; + try { + validate = await testAjv.compileAsync(schema); + } catch (error) { + return `${filename}: Failed to compile schema for example validation: ${error.message}`; + } + + // Validate each example + for (let i = 0; i < schema.examples.length; i++) { + const example = schema.examples[i]; + const exampleData = example.data || example; + + const valid = validate(exampleData); + if (!valid) { + const errors = validate.errors.map(err => + `${err.instancePath} ${err.message}` + ).join('; '); + return `${filename}: Example ${i + 1} ${example.description ? `"${example.description}" ` : ''}failed validation: ${errors}`; + } + } + } + return true; + }); + // Print results log('\n=========================================='); log(`Tests completed: ${totalTests}`);