Creative Manifests
Complete guide to AdCP creative manifest structure, asset types, and validation.
Overview
AdCP creative manifests provide a standardized way to describe creative assets and their requirements. Instead of sending pre-assembled markup, bidders provide a manifest that publishers use to assemble and render the creative.
Manifest Structure
AdCP creative manifests follow a standardized structure where assets are keyed
by asset_id:
{
"format_id": {
"agent_url": "https://creative.adcontextprotocol.org",
"id": "display_300x250"
},
"promoted_offering": "Spring Sale Collection",
"assets": {
"banner_image": {
"url": "https://cdn.brand.com/banner.jpg",
"width": 300,
"height": 250,
"format": "jpg"
},
"headline": {
"content": "50% Off Spring Sale"
},
"clickthrough_url": {
"url": "https://brand.com/spring?campaign={MEDIA_BUY_ID}"
},
"impression_tracker": {
"url": "https://track.brand.com/imp?buy={MEDIA_BUY_ID}&cb={CACHEBUSTER}"
}
}
}
Root-Level Fields
| Field | Type | Required | Description |
|---|---|---|---|
format_id | object | Yes | Format identifier matching request |
format_id.agent_url | string | Yes | Creative agent URL |
format_id.id | string | Yes | Format ID |
promoted_offering | string | No | Product/service being advertised |
assets | object | Yes | Creative assets keyed by asset_id |
Asset Structure
Assets are keyed by asset_id (which matches the format's assets_required
array). Each asset has type-specific fields:
Image Assets
{
"banner_image": {
"url": "https://cdn.brand.com/banner.jpg",
"width": 300,
"height": 250,
"format": "jpg",
"alt_text": "Spring Sale Banner"
}
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Image URL |
width | integer | Yes | Image width in pixels |
height | integer | Yes | Image height in pixels |
format | string | Yes | Image format (jpg, png, gif) |
alt_text | string | No | Alternative text for accessibility |
Text Assets
{
"headline": {
"content": "50% Off Spring Sale"
},
"description": {
"content": "Limited time offer on all spring items"
}
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes | Text content |
URL Assets
{
"clickthrough_url": {
"url": "https://brand.com/spring?campaign={MEDIA_BUY_ID}"
}
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | URL (may include universal macros) |
Tracking Assets
{
"impression_tracker": {
"url": "https://track.brand.com/imp?buy={MEDIA_BUY_ID}&cb={CACHEBUSTER}"
},
"click_tracker": {
"url": "https://track.brand.com/click?buy={MEDIA_BUY_ID}&cb={CACHEBUSTER}"
}
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Tracking URL (may include macros) |
Complete Example
Display Banner Manifest
{
"format_id": {
"agent_url": "https://creative.adcontextprotocol.org",
"id": "display_728x90"
},
"promoted_offering": "Premium Travel Package",
"assets": {
"banner_image": {
"url": "https://cdn.travel.com/banner-728x90.jpg",
"width": 728,
"height": 90,
"format": "jpg",
"alt_text": "Luxury beach resort"
},
"headline": {
"content": "Escape to Paradise"
},
"description": {
"content": "All-inclusive luxury resort from $299/night"
},
"clickthrough_url": {
"url": "https://travel.com/resort?campaign={MEDIA_BUY_ID}&device={DEVICE_TYPE}"
},
"impression_tracker": {
"url": "https://track.travel.com/imp?buy={MEDIA_BUY_ID}&cb={CACHEBUSTER}"
},
"click_tracker": {
"url": "https://track.travel.com/click?buy={MEDIA_BUY_ID}&cb={CACHEBUSTER}"
}
}
}
Native Ad Manifest
{
"format_id": {
"agent_url": "https://creative.adcontextprotocol.org",
"id": "native_article"
},
"promoted_offering": "Smart Home Security System",
"assets": {
"title": {
"content": "Protect Your Home with AI-Powered Security"
},
"description": {
"content": "24/7 monitoring with facial recognition and instant alerts"
},
"main_image": {
"url": "https://cdn.security.com/product.jpg",
"width": 1200,
"height": 628,
"format": "jpg",
"alt_text": "Smart security camera"
},
"icon": {
"url": "https://cdn.security.com/logo.png",
"width": 100,
"height": 100,
"format": "png",
"alt_text": "SecureHome logo"
},
"sponsor": {
"content": "SecureHome"
},
"cta_text": {
"content": "Learn More"
},
"clickthrough_url": {
"url": "https://securehome.com/product?campaign={MEDIA_BUY_ID}"
},
"impression_tracker": {
"url": "https://track.securehome.com/imp?buy={MEDIA_BUY_ID}&cb={CACHEBUSTER}"
}
}
}
Bid Response Integration
Creative manifests are included in bid responses within
bid.ext.aura.adcpFormat:
{
"bid": {
"id": "bid-001",
"impid": "imp-1",
"price": 2.5,
"adm": "<a href='https://brand.com'><img src='https://cdn.com/banner.jpg'/></a>",
"ext": {
"aura": {
"adcpFormat": {
"formatId": {
"agent_url": "https://creative.adcontextprotocol.org",
"id": "display_300x250"
},
"creativeManifest": {
"format_id": {
"agent_url": "https://creative.adcontextprotocol.org",
"id": "display_300x250"
},
"promoted_offering": "Product Name",
"assets": {
"banner_image": {
"url": "https://cdn.brand.com/banner.jpg",
"width": 300,
"height": 250,
"format": "jpg"
},
"clickthrough_url": {
"url": "https://brand.com/product?campaign={MEDIA_BUY_ID}"
},
"impression_tracker": {
"url": "https://track.brand.com/imp?buy={MEDIA_BUY_ID}&cb={CACHEBUSTER}"
}
}
}
}
}
}
}
}
Validation
Publishers should validate creative manifests against format definitions:
1. Format ID Validation
function validateFormatId(manifest, requestedFormats) {
const formatId = `${manifest.format_id.agent_url}/${manifest.format_id.id}`
const requested = requestedFormats.map(f => `${f.agent_url}/${f.id}`)
if (!requested.includes(formatId)) {
throw new Error(`Format ${formatId} not requested`)
}
}
2. Required Assets Validation
function validateRequiredAssets(manifest, formatDefinition) {
const requiredAssets = formatDefinition.assets_required
.filter(a => a.required)
.map(a => a.asset_id)
for (const assetId of requiredAssets) {
if (!manifest.assets[assetId]) {
throw new Error(`Required asset ${assetId} missing`)
}
}
}
3. Asset Type Validation
function validateAssetTypes(manifest, formatDefinition) {
for (const [assetId, asset] of Object.entries(manifest.assets)) {
const assetDef = formatDefinition.assets_required.find(
a => a.asset_id === assetId
)
if (!assetDef) {
console.warn(`Unknown asset ${assetId}`)
continue
}
// Validate based on asset type
switch (assetDef.asset_type) {
case 'image':
validateImageAsset(asset, assetDef.requirements)
break
case 'text':
validateTextAsset(asset, assetDef.requirements)
break
case 'url':
validateUrlAsset(asset)
break
}
}
}
4. Image Asset Validation
function validateImageAsset(asset, requirements) {
// Check dimensions
if (requirements.width && asset.width !== requirements.width) {
throw new Error(
`Image width ${asset.width} doesn't match required ${requirements.width}`
)
}
if (requirements.height && asset.height !== requirements.height) {
throw new Error(
`Image height ${asset.height} doesn't match required ${requirements.height}`
)
}
// Check format
if (
requirements.acceptable_formats &&
!requirements.acceptable_formats.includes(asset.format)
) {
throw new Error(`Image format ${asset.format} not acceptable`)
}
// Check URL is valid
if (!isValidUrl(asset.url)) {
throw new Error(`Invalid image URL: ${asset.url}`)
}
}
Brand Manifest
Brand Manifests provide brand identity and context for creative generation. They can be included inline or referenced by URL.
Inline Brand Manifest
{
"brand_manifest": {
"url": "https://mybrand.com",
"name": "My Brand",
"colors": {
"primary": "#FF6B35",
"secondary": "#004E89"
},
"fonts": {
"primary": "Helvetica Neue",
"secondary": "Georgia"
},
"tone": "professional and trustworthy",
"tagline": "Innovation You Can Trust"
}
}
Brand Manifest by URL Reference
{
"brand_manifest": "https://cdn.mybrand.com/brand-manifest.json"
}
Minimal Brand Manifest
{
"brand_manifest": {
"url": "https://mybrand.com"
}
}
Best Practices
1. Always Provide Fallback
Include standard OpenRTB adm field as fallback:
{
"bid": {
"adm": "<a href='...'><img src='...'/></a>",
"ext": {
"aura": {
"adcpFormat": {
"creativeManifest": {
/* ... */
}
}
}
}
}
}
2. Validate Before Sending
Validate manifests against format definitions before including in bid responses:
function validateManifest(manifest, formatDefinition) {
validateFormatId(manifest, formatDefinition)
validateRequiredAssets(manifest, formatDefinition)
validateAssetTypes(manifest, formatDefinition)
return true
}
4. Optimize Asset URLs
Use CDN URLs for all assets to ensure fast loading:
{
"banner_image": {
"url": "https://cdn.brand.com/banner.jpg" // ✅ CDN URL
}
}
5. Include Alt Text
Always include alt text for images for accessibility:
{
"banner_image": {
"url": "https://cdn.brand.com/banner.jpg",
"alt_text": "Luxury beach resort with palm trees"
}
}
Next Steps
- Tracking - Tracking implementation
- Format Support - Declaring format support
- Contextual Weaving Format - AI-native format details