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
45 changes: 45 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,51 @@ For major version changes:
- Run tests with `npm test` before committing
- Ensure new features have corresponding tests

### Format Field Naming Convention

**CRITICAL**: Always use consistent naming for format-related fields to avoid developer confusion.

**Established Convention**:
- **`"formats"`** = Array of format objects (with full details like name, type, requirements, assets_required, etc.)
- **`"format_ids"`** = Array of format ID strings (references to format objects)
- **`"format_types"`** = Array of high-level type strings (video, display, audio, native, etc.)

**Examples**:
```json
// ✅ CORRECT - list_creative_formats response (format objects)
{
"formats": [
{
"format_id": "video_standard_30s",
"name": "Standard Video - 30 seconds",
"type": "video",
"requirements": {...}
}
]
}

// ✅ CORRECT - Product response (format ID strings)
{
"product_id": "ctv_premium",
"format_ids": ["video_standard_30s", "video_standard_15s"]
}

// ✅ CORRECT - get_products filter (high-level types)
{
"filters": {
"format_types": ["video", "display"]
}
}
```

**When adding new fields**:
- Use `format_ids` when referencing existing formats by ID
- Use `formats` only when returning full format objects
- Use `format_types` for broad categorical filtering
- Never use `formats` for arrays of strings - always use `format_ids`

**Schema Validation**: All schemas must follow this convention. Tests will fail if format fields don't match the expected naming pattern.

## Common Tasks

### Before Making Changes
Expand Down
12 changes: 6 additions & 6 deletions docs/media-buy/media-products.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ A server can offer a general catalog, but it can also return:
"product_id": "connected_tv_prime",
"name": "Connected TV - Prime Time",
"description": "Premium CTV inventory 8PM-11PM",
"formats": [{"format_id": "video_standard", "name": "Standard Video"}],
"format_ids": ["video_standard"],
"delivery_type": "guaranteed",
"is_fixed_price": true,
"cpm": 45.00
Expand All @@ -99,7 +99,7 @@ A server can offer a general catalog, but it can also return:
"product_id": "custom_abc123",
"name": "Custom - Gaming Enthusiasts",
"description": "Custom audience package for gaming campaign",
"formats": [{"format_id": "display_300x250", "name": "Medium Rectangle"}],
"format_ids": ["display_300x250"],
"delivery_type": "non_guaranteed",
"is_fixed_price": false,
"price_guidance": {
Expand All @@ -118,10 +118,10 @@ A server can offer a general catalog, but it can also return:
"product_id": "albertsons_pet_category_offsite",
"name": "Pet Category Shoppers - Offsite Display & Video",
"description": "Target Albertsons shoppers who have purchased pet products in the last 90 days. Reach them across premium display and video inventory.",
"formats": [
{"format_id": "display_300x250", "name": "Medium Rectangle"},
{"format_id": "display_728x90", "name": "Leaderboard"},
{"format_id": "video_15s_vast", "name": "15-second VAST"}
"format_ids": [
"display_300x250",
"display_728x90",
"video_15s_vast"
],
"delivery_type": "guaranteed",
"is_fixed_price": true,
Expand Down
16 changes: 8 additions & 8 deletions docs/media-buy/tasks/create_media_buy.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Create a media buy from selected packages. This task handles the complete workfl
|-----------|------|----------|-------------|
| `buyer_ref` | string | Yes | Buyer's reference identifier for this package |
| `products` | string[] | Yes | Array of product IDs to include in this package |
| `formats` | string[] | Yes | Array of format IDs that will be used for this package - must be supported by all products |
| `format_ids` | string[] | Yes | Array of format IDs that will be used for this package - must be supported by all products |
| `budget` | Budget | No | Budget configuration for this package (overrides media buy level budget if specified) |
| `targeting_overlay` | TargetingOverlay | No | Additional targeting criteria for this package (see Targeting Overlay Object below) |

Expand Down Expand Up @@ -121,7 +121,7 @@ The AdCP payload is identical across protocols. Only the request/response wrappe
{
"buyer_ref": "nike_ctv_sports_package",
"products": ["ctv_sports_premium", "ctv_prime_time"],
"formats": ["video_standard_30s", "video_standard_15s"],
"format_ids": ["video_standard_30s", "video_standard_15s"],
"budget": {
"total": 60000,
"currency": "USD",
Expand All @@ -136,7 +136,7 @@ The AdCP payload is identical across protocols. Only the request/response wrappe
{
"buyer_ref": "nike_audio_drive_package",
"products": ["audio_drive_time"],
"formats": ["audio_standard_30s"],
"format_ids": ["audio_standard_30s"],
"budget": {
"total": 40000,
"currency": "USD",
Expand Down Expand Up @@ -257,7 +257,7 @@ await a2a.send({
{
"buyer_ref": "nike_ctv_sports_package",
"products": ["ctv_sports_premium", "ctv_prime_time"],
"formats": ["video_standard_30s", "video_standard_15s"],
"format_ids": ["video_standard_30s", "video_standard_15s"],
"budget": {
"total": 60000,
"currency": "USD",
Expand All @@ -272,7 +272,7 @@ await a2a.send({
{
"buyer_ref": "nike_audio_drive_package",
"products": ["audio_drive_time"],
"formats": ["audio_standard_30s"],
"format_ids": ["audio_standard_30s"],
"budget": {
"total": 40000,
"currency": "USD",
Expand Down Expand Up @@ -570,7 +570,7 @@ data: {"status": {"state": "completed"}, "artifacts": [...]}
{
"buyer_ref": "purina_ctv_package",
"products": ["ctv_prime_time", "ctv_late_night"],
"formats": ["video_standard_30s"],
"format_ids": ["video_standard_30s"],
"budget": {
"total": 30000,
"currency": "USD",
Expand All @@ -590,7 +590,7 @@ data: {"status": {"state": "completed"}, "artifacts": [...]}
{
"buyer_ref": "purina_audio_package",
"products": ["audio_drive_time"],
"formats": ["audio_standard_30s"],
"format_ids": ["audio_standard_30s"],
"budget": {
"total": 20000,
"currency": "USD"
Expand Down Expand Up @@ -621,7 +621,7 @@ data: {"status": {"state": "completed"}, "artifacts": [...]}
{
"buyer_ref": "purina_albertsons_conquest",
"products": ["albertsons_competitive_conquest", "albertsons_onsite_display"],
"formats": ["display_300x250", "display_728x90"],
"format_ids": ["display_300x250", "display_728x90"],
"budget": {
"total": 75000,
"currency": "USD",
Expand Down
19 changes: 9 additions & 10 deletions docs/media-buy/tasks/get_products.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ Discover available advertising products based on campaign requirements, using na
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `delivery_type` | string | No | Filter by delivery type: `"guaranteed"` or `"non_guaranteed"` |
| `formats` | string[] | No | Filter by specific formats (e.g., `["video"]`) |
| `is_fixed_price` | boolean | No | Filter for fixed price vs auction products |
| `format_types` | string[] | No | Filter by format types (e.g., `["video", "display"]`) |
| `format_ids` | string[] | No | Filter by specific format IDs (e.g., `["video_standard_30s"]`) |
Expand All @@ -49,7 +48,7 @@ The message is returned differently in each protocol:
"product_id": "string",
"name": "string",
"description": "string",
"formats": [
"format_ids": [
"format_id_string"
],
"delivery_type": "string",
Expand Down Expand Up @@ -77,7 +76,7 @@ The message is returned differently in each protocol:
- **product_id**: Unique identifier for the product
- **name**: Human-readable product name
- **description**: Detailed description of the product and its inventory
- **formats**: Array of supported creative format IDs (strings) - use `list_creative_formats` to get full format details
- **format_ids**: Array of supported creative format IDs (strings) - use `list_creative_formats` to get full format details
- **delivery_type**: Either `"guaranteed"` or `"non_guaranteed"`
- **is_fixed_price**: Whether this product has fixed pricing (true) or uses auction (false)
- **cpm**: Cost per thousand impressions in USD
Expand Down Expand Up @@ -119,7 +118,7 @@ The AdCP payload is identical across protocols. Only the request/response wrappe
"product_id": "ctv_sports_premium",
"name": "CTV Sports Premium",
"description": "Premium CTV inventory on sports content",
"formats": ["video_16x9_30s"],
"format_ids": ["video_16x9_30s"],
"delivery_type": "guaranteed",
"is_fixed_price": true,
"cpm": 45.00,
Expand Down Expand Up @@ -191,7 +190,7 @@ A2A returns results as artifacts with text and data parts:
"product_id": "ctv_sports_premium",
"name": "CTV Sports Premium",
"description": "Premium CTV inventory on sports content",
"formats": ["video_16x9_30s"],
"format_ids": ["video_16x9_30s"],
"delivery_type": "guaranteed",
"is_fixed_price": true,
"cpm": 45.00,
Expand Down Expand Up @@ -239,7 +238,7 @@ A2A returns results as artifacts with text and data parts:
"promoted_offering": "Peloton Digital Membership - unlimited access to live and on-demand fitness classes, promoting New Year special pricing",
"filters": {
"delivery_type": "guaranteed",
"formats": ["video"],
"format_types": ["video"],
"is_fixed_price": true,
"standard_formats_only": true
}
Expand All @@ -264,7 +263,7 @@ A2A returns results as artifacts with text and data parts:
"product_id": "open_exchange_video",
"name": "Open Exchange - Video",
"description": "Programmatic video inventory across all publishers",
"formats": ["video_standard"],
"format_ids": ["video_standard"],
"delivery_type": "non_guaranteed",
"is_fixed_price": false,
"cpm": 12.00,
Expand All @@ -287,7 +286,7 @@ A2A returns results as artifacts with text and data parts:
"product_id": "connected_tv_prime",
"name": "Connected TV - Prime Time",
"description": "Premium CTV inventory 8PM-11PM",
"formats": ["video_standard"],
"format_ids": ["video_standard"],
"delivery_type": "guaranteed",
"is_fixed_price": true,
"cpm": 45.00,
Expand All @@ -309,7 +308,7 @@ A2A returns results as artifacts with text and data parts:
"product_id": "albertsons_pet_category_syndicated",
"name": "Pet Category Shoppers - Syndicated",
"description": "Target Albertsons shoppers who have purchased pet products in the last 90 days across offsite display and video inventory.",
"formats": [
"format_ids": [
"display_300x250",
"video_15s_vast"
],
Expand All @@ -335,7 +334,7 @@ A2A returns results as artifacts with text and data parts:
"product_id": "albertsons_custom_competitive_conquest",
"name": "Custom: Competitive Dog Food Buyers",
"description": "Custom audience of Albertsons shoppers who buy competitive dog food brands. Higher precision targeting for conquest campaigns.",
"formats": [
"format_ids": [
"display_300x250",
"display_728x90"
],
Expand Down
4 changes: 2 additions & 2 deletions static/schemas/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const product = {
"product_id": "ctv_sports_premium",
"name": "CTV Sports Premium",
"description": "Premium CTV inventory on sports content",
"formats": [{"format_id": "video_16x9_30s", "name": "30-second video"}],
"format_ids": ["video_16x9_30s"],
"delivery_type": "guaranteed",
"is_fixed_price": true
};
Expand All @@ -120,7 +120,7 @@ product = {
"product_id": "ctv_sports_premium",
"name": "CTV Sports Premium",
"description": "Premium CTV inventory on sports content",
"formats": [{"format_id": "video_16x9_30s", "name": "30-second video"}],
"format_ids": ["video_16x9_30s"],
"delivery_type": "guaranteed",
"is_fixed_price": True
}
Expand Down
4 changes: 2 additions & 2 deletions static/schemas/v1/core/product.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"type": "string",
"description": "Detailed description of the product and its inventory"
},
"formats": {
"format_ids": {
"type": "array",
"description": "Array of supported creative format IDs - use list_creative_formats to get full format details",
"items": {
Expand Down Expand Up @@ -66,7 +66,7 @@
"product_id",
"name",
"description",
"formats",
"format_ids",
"delivery_type",
"is_fixed_price"
],
Expand Down
4 changes: 2 additions & 2 deletions static/schemas/v1/media-buy/create-media-buy-request.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"type": "string"
}
},
"formats": {
"format_ids": {
"type": "array",
"description": "Array of format IDs that will be used for this package - must be supported by all products",
"items": {
Expand All @@ -48,7 +48,7 @@
}
},
"anyOf": [
{"required": ["buyer_ref", "products", "formats"]},
{"required": ["buyer_ref", "products", "format_ids"]},
{"required": ["buyer_ref", "products", "format_selection"]}
],
"additionalProperties": false
Expand Down
7 changes: 0 additions & 7 deletions static/schemas/v1/media-buy/get-products-request.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@
"delivery_type": {
"$ref": "/schemas/v1/enums/delivery-type.json"
},
"formats": {
"type": "array",
"description": "Filter by specific formats",
"items": {
"type": "string"
}
},
"is_fixed_price": {
"type": "boolean",
"description": "Filter for fixed price vs auction products"
Expand Down
2 changes: 1 addition & 1 deletion static/schemas/validation-example.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const exampleProduct = {
"product_id": "ctv_sports_premium",
"name": "CTV Sports Premium",
"description": "Premium CTV inventory on sports content",
"formats": [{"format_id": "video_16x9_30s", "name": "30-second video"}],
"format_ids": ["video_16x9_30s"],
"delivery_type": "guaranteed",
"is_fixed_price": true
};
Expand Down
4 changes: 2 additions & 2 deletions tests/example-validation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const exampleData = {
"product_id": "ctv_sports_premium",
"name": "CTV Sports Premium",
"description": "Premium CTV inventory on sports content",
"formats": [{"format_id": "video_16x9_30s", "name": "30-second video"}],
"format_ids": ["video_16x9_30s"],
"delivery_type": "guaranteed",
"is_fixed_price": true,
"cpm": 45.00,
Expand Down Expand Up @@ -201,7 +201,7 @@ const exampleData = {
"product_id": "ctv_sports_premium",
"name": "CTV Sports Premium",
"description": "Premium CTV inventory on sports content",
"formats": [{"format_id": "video_16x9_30s", "name": "30-second video"}],
"format_ids": ["video_16x9_30s"],
"delivery_type": "guaranteed",
"is_fixed_price": true,
"cpm": 45.00,
Expand Down
2 changes: 1 addition & 1 deletion tests/schema-validation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ async function runTests() {
await test('Core schemas have appropriate required fields', () => {
const coreSchemas = schemas.filter(([path]) => path.includes('/core/'));
const requiredFieldChecks = {
'product.json': ['product_id', 'name', 'description', 'formats', 'delivery_type', 'is_fixed_price'],
'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'],
Expand Down