Skip to main content

Migrating catalogs

AdCP 3.0 removes the promoted_offerings creative asset type and the promoted_offering string field from media buys and creative manifests. Catalogs are now first-class protocol objects with their own sync task, format-level requirements, and conversion event alignment.

What changed

v2 fieldv3 fieldChange type
promoted_offerings (creative asset type)catalog assets in creative manifest assetsReplaced
promoted_offering (string on media-buy)RemovedRemoved
promoted_offering (string on creative-manifest)catalog assets in assetsReplaced
No catalog syncsync_catalogs taskNew
No format catalog requirementscatalog asset type on Format assetsNew
No catalog-event linkingconversion_events on CatalogNew

Creative manifests

What changed

In v2, catalog data was embedded inside creative_manifest.assets as a promoted_offerings object that bundled brand identity, inline offerings, and SI agent URL together. In v3, brand identity is a first-class parameter on tasks (build_creative, sync_creatives), and catalogs are included as catalog asset types in the manifest’s assets.

Before (v2)

{
  "creative_id": "product-carousel",
  "format_id": {
    "agent_url": "https://creatives.example.com",
    "id": "product_carousel"
  },
  "assets": {
    "promoted_offerings": {
      "brand": {
        "domain": "acmecorp.example.com"
      },
      "offerings": [
        {
          "offering_id": "winter-sale",
          "name": "Winter Sale Collection",
          "description": "50% off all winter items"
        }
      ]
    }
  }
}

After (v3)

{
  "$schema": "/schemas/core/creative-manifest.json",
  "format_id": {
    "agent_url": "https://creatives.example.com",
    "id": "product_carousel"
  },
  "assets": {
    "product_catalog": {
      "type": "product",
      "catalog_id": "winter-products"
    }
  }
}
Key differences:
  • Brand identity comes from the brand parameter on the task, not embedded in creative assets
  • Catalogs are catalog asset types in the assets map, keyed by a role name (e.g., product_catalog)
  • Catalog references synced data by catalog_id instead of inlining items (though inline items are still supported for simple cases)

Media buys

What changed

The promoted_offering string field is removed from media buy objects. What’s being promoted is now expressed through the brief on get_products/create_media_buy and through catalog references on creatives.

Before (v2)

{
  "media_buy_id": "mb_123",
  "promoted_offering": "Winter Sale Collection",
  "status": "draft",
  "total_budget": {
    "amount": 10000,
    "currency": "USD"
  },
  "packages": []
}

After (v3)

{
  "media_buy_id": "mb_123",
  "status": "draft",
  "total_budget": {
    "amount": 10000,
    "currency": "USD"
  },
  "packages": []
}
If you were using promoted_offering for reporting or display purposes, that context now comes from:
  • The brief field on get_products and create_media_buy
  • The brand parameter on tasks
  • Catalog assets in creative manifest assets

Syncing catalogs (new in v3)

The sync_catalogs task lets buyers push catalog data to sellers before submitting creatives. This replaces the pattern of embedding product data inside creative assets. Key features:
  • Bulk sync with per-catalog results
  • Async approval workflow (working, input-required, submitted)
  • Item-level review with approve/reject per catalog item
  • Discovery mode — omit the catalogs field to see existing synced catalogs
  • Validation modes — strict, lenient, or dry_run

Workflow

list_creative_formats → check catalog assets → sync_catalogs → sync_creatives with catalog assets
  1. Discover what catalogs a format needs via catalog asset types in the format’s assets
  2. Sync those catalogs using sync_catalogs
  3. Wait for approval if the seller requires review
  4. Submit creatives that reference the synced catalog_id

Catalogs documentation

Complete reference including all 13 catalog types, sync workflow, and item-level review.

Format catalog requirements (new in v3)

Formats declare catalog needs as catalog asset types in their assets array:
{
  "assets": [
    {
      "item_type": "individual",
      "asset_id": "product_catalog",
      "asset_type": "catalog",
      "required": true,
      "requirements": {
        "catalog_type": "product",
        "min_items": 3,
        "required_fields": ["name", "price", "image_url"]
      }
    }
  ]
}
Format declarations use asset_type: "catalog" with a requirements object containing:
FieldTypeDescription
catalog_typestringRequired. The catalog type (e.g., product, store, job)
min_itemsintegerMinimum items the catalog must contain
max_itemsintegerMaximum items the format can render
required_fieldsstring[]Fields that must be present on every item
feed_formatsstring[]Accepted feed formats (e.g., google_merchant_center, linkedin_jobs)
This replaces the v2 pattern where formats implicitly required promoted_offerings in their assets — the requirement is now explicit and discoverable.

Creative agent migration

Creative agents that read format definitions to determine catalog requirements need to change how they discover and fulfill catalog slots. Before (v2) — checking a dedicated catalog_requirements field:
// v2: catalog requirements were a separate top-level array
for (const req of format.catalog_requirements) {
  const catalogType = req.catalog_type;
  const minItems = req.min_items;
}
After (v3) — iterating the assets array and filtering by asset_type:
// v3: catalogs are assets like any other
const catalogAssets = format.assets.filter(a => a.asset_type === "catalog");
for (const slot of catalogAssets) {
  const catalogType = slot.requirements.catalog_type;
  const minItems = slot.requirements.min_items;
  const slotId = slot.asset_id; // use as key in manifest.assets
}
When building a manifest, catalog assets are keyed by their asset_id from the format definition:
{
  "assets": {
    "product_catalog": {
      "type": "product",
      "catalog_id": "winter-products"
    }
  }
}
The asset_id (e.g., product_catalog) from the format’s assets array becomes the key in the manifest’s assets object.

Conversion event alignment (new in v3)

Catalogs declare which event types represent conversions for their items:
{
  "catalog_id": "job-feed",
  "type": "job",
  "content_id_type": "job_id",
  "conversion_events": ["submit_application", "complete_registration"]
}
This links catalogs to the conversion tracking system. When a log_event is sent with content_ids matching catalog items, the platform knows which events to attribute. The content_id_type field declares what identifier type content_ids values represent. For vertical catalogs, this matches the item’s canonical ID field (job_id, hotel_id, etc.). For product catalogs, it distinguishes between sku and gtin for cross-retailer matching. Omit when using a custom identifier scheme.
Catalog typeTypical conversion events
productpurchase, add_to_cart
hotelpurchase (booking)
flightpurchase (booking)
jobsubmit_application
vehiclelead, schedule
real_estatelead, schedule
educationsubmit_application, complete_registration
destinationpurchase (booking)

Conversion events

Full documentation on catalog-event alignment.

product_selectors replaced by catalog on get_products

The product_selectors field on get_products has been replaced by catalog. The PromotedProducts schema has been removed.

Before

{
  "brand": { "domain": "acmecorp.com" },
  "product_selectors": {
    "manifest_gtins": ["00013000006040"],
    "manifest_tags": ["organic"]
  }
}

After

{
  "brand": { "domain": "acmecorp.com" },
  "catalog": {
    "type": "product",
    "gtins": ["00013000006040"],
    "tags": ["organic"]
  }
}

Field mapping

Old (product_selectors)New (catalog)
manifest_gtinsgtins
manifest_skusids
manifest_tagstags
manifest_categorycategory
manifest_queryquery

Response changes

  • product_selectors_applied is now catalog_applied
  • catalog_match.matched_skus is now catalog_match.matched_ids

New fields on Product

  • catalog_types — Array of catalog types this product supports (e.g., ["product"], ["job", "offering"])
  • catalog_match.matched_ids — Generic item ID matches (replaces matched_skus)
  • catalog_match.matched_count — Count of matched items

Catalog on packages

Packages now accept a catalog field for catalog-driven campaigns. One budget envelope promotes an entire catalog, with the platform optimizing delivery across items.

Store catchment targeting

targeting_overlay now supports store_catchments — referencing synced store catalogs for proximity targeting.

Per-catalog-item delivery

get_media_buy_delivery now includes by_catalog_item breakdowns within packages for catalog-driven campaigns.

Migration steps

1

Remove promoted_offerings from creative assets

Remove promoted_offerings objects from creative_manifest.assets. Brand identity is now provided via the brand task parameter.
2

Add catalog assets to creative manifests

For creatives that render catalog items (product carousels, store locators, etc.), add catalog assets to the manifest’s assets map. Each catalog asset is a catalog object with type and catalog_id (e.g., { "type": "product", "catalog_id": "winter-products" }).
3

Remove promoted_offering from media buys

Remove the promoted_offering string from create_media_buy requests. Use the brief field to describe what’s being promoted.
4

Discover catalog requirements

Call list_creative_formats and check for catalog asset types in each format’s assets to understand what catalog types and fields are needed.
5

Sync catalogs before submitting creatives

Use the sync_catalogs task to push catalog data to sellers. Handle async approval if the seller requires review.
6

Add conversion events and content ID type for attribution

Include conversion_events on catalogs to link catalog items to your conversion tracking setup. Add content_id_type to declare what identifier type (e.g., sku, gtin, job_id) the event’s content_ids should be matched against.
7

Validate against v3 schemas

Ensure creative manifests, media buys, and catalog objects validate against v3 schemas.

Related: Channels | Pricing | Geo targeting | Creatives | Attribution | AdCP 3.0 overview