Skip to content

Commit 9de1eda

Browse files
bokelleyclaude
andauthored
feat: sync upstream catalog schemas and add catalog type support (#134)
* feat: sync upstream catalog schemas and add catalog type support Pulls latest ADCP schemas which introduce a new catalog system for syncing product feeds, offerings, and inventory with ad platforms. New types: - Catalog, CatalogType (12 types: product/offering/inventory/store/etc.) - CatalogAction, CatalogItemStatus enums - FeedFormat (google_merchant_center/facebook/shopify/linkedin/custom) - UpdateFrequency (realtime/hourly/daily/weekly) - ContentIdType, Gtin, OfferingAssetConstraint, OfferingAssetGroup - CatalogRequirements (format requirements for platform catalog specs) - SyncCatalogsRequest/Response + async variants (InputRequired/Submitted/Working) - SyncCatalogsSuccessResponse, SyncCatalogsErrorResponse aliases - SyncCatalogResult alias (per-catalog result type from success response) Backward compat: - PromotedOfferings, PromotedOfferingsAssetRequirements, PromotedOfferingsRequirement, PromotedProducts, AssetSelectors are removed upstream but kept as permissive stubs (extra="allow") Script changes: - consolidate_exports.py: process enums/ first so canonical enum definitions take priority over inline duplicates in domain models; document intentional Catalog name collision Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: sort ContentIdType import alphabetically in types/__init__.py Fixes ruff I001 import sort error that caused CI failures. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: preserve PromotedOfferingsRequirement as enum stub The type was previously an enum (si_agent_url, offerings, brand.logos, etc.). Replacing it with a model stub silently broke attribute access and iteration. Restore it as a str+Enum with the original 7 values so existing code continues to work without runtime surprises. Also add noqa: F401 to the generated_poc side-effect import in _generated.py to fix the ruff warning. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: sync ADCP schemas - catalogs array on creative types, UniversalMacro additions Changes from upstream schema sync: - CreativeAsset/CreativeManifest/Creative: catalog (singular) → catalogs (list) Supporting multiple catalogs per creative (e.g. one per format requirement type) - UniversalMacro: add 13 new catalog-item macros (CATALOG_ID, SKU, GTIN, OFFERING_ID, JOB_ID, HOTEL_ID, FLIGHT_ID, VEHICLE_ID, LISTING_ID, STORE_ID, PROGRAM_ID, DESTINATION_ID, CREATIVE_VARIANT_ID) - extensions/index.json: minor update - sync-creatives-request.json: minor update Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: add coverage for catalogs field on creative types and UniversalMacro additions - Creative/CreativeAsset/CreativeManifest: test catalogs field accepts list and rejects empty list (min_length=1 constraint) - UniversalMacro: regression test for 13 new catalog-item macro values (CATALOG_ID, SKU, GTIN, OFFERING_ID, JOB_ID, HOTEL_ID, FLIGHT_ID, VEHICLE_ID, LISTING_ID, STORE_ID, PROGRAM_ID, DESTINATION_ID, CREATIVE_VARIANT_ID) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 08fe80b commit 9de1eda

File tree

126 files changed

+6293
-1033
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+6293
-1033
lines changed

schemas/cache/.hashes.json

Lines changed: 58 additions & 36 deletions
Large diffs are not rendered by default.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"additionalProperties": true,
4+
"description": "Markdown-formatted text content following CommonMark specification",
5+
"properties": {
6+
"allow_raw_html": {
7+
"default": false,
8+
"description": "Whether raw HTML blocks are allowed in the markdown. False recommended for security.",
9+
"type": "boolean"
10+
},
11+
"content": {
12+
"description": "Markdown content following CommonMark spec with optional GitHub Flavored Markdown extensions",
13+
"type": "string"
14+
},
15+
"language": {
16+
"description": "Language code (e.g., 'en', 'es', 'fr')",
17+
"type": "string"
18+
},
19+
"markdown_flavor": {
20+
"$ref": "../../enums/markdown-flavor.json",
21+
"default": "commonmark",
22+
"description": "Markdown flavor used. CommonMark for strict compatibility, GFM for tables/task lists/strikethrough."
23+
}
24+
},
25+
"required": [
26+
"content"
27+
],
28+
"title": "Markdown Asset",
29+
"type": "object"
30+
}

schemas/cache/core/async-response-data.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,26 @@
8080
"$ref": "../media-buy/sync-creatives-async-response-submitted.json",
8181
"description": "Acknowledgment for submitted sync_creatives",
8282
"title": "SyncCreativesAsyncSubmitted"
83+
},
84+
{
85+
"$ref": "../media-buy/sync-catalogs-response.json",
86+
"description": "Response for completed or failed sync_catalogs",
87+
"title": "SyncCatalogsResponse"
88+
},
89+
{
90+
"$ref": "../media-buy/sync-catalogs-async-response-working.json",
91+
"description": "Progress data for working sync_catalogs",
92+
"title": "SyncCatalogsAsyncWorking"
93+
},
94+
{
95+
"$ref": "../media-buy/sync-catalogs-async-response-input-required.json",
96+
"description": "Input requirements for sync_catalogs needing buyer input",
97+
"title": "SyncCatalogsAsyncInputRequired"
98+
},
99+
{
100+
"$ref": "../media-buy/sync-catalogs-async-response-submitted.json",
101+
"description": "Acknowledgment for submitted sync_catalogs",
102+
"title": "SyncCatalogsAsyncSubmitted"
83103
}
84104
],
85105
"description": "Union of all possible data payloads for async task webhook responses. For completed/failed statuses, use the main task response schema. For working/input-required/submitted, use the status-specific schemas.",

schemas/cache/core/catalog.json

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"additionalProperties": true,
4+
"description": "A typed data feed. Catalogs carry the items, locations, stock levels, or pricing that publishers use to render ads. They can be synced to a platform via sync_catalogs (managed lifecycle with approval), provided inline, or fetched from an external URL. The catalog type determines the item schema and can be structural (offering, product, inventory, store, promotion) or vertical-specific (hotel, flight, job, vehicle, real_estate, education, destination). Selectors (ids, tags, category, query) filter items regardless of sourcing method.",
5+
"examples": [
6+
{
7+
"data": {
8+
"catalog_id": "gmc-primary",
9+
"feed_format": "google_merchant_center",
10+
"name": "Primary Product Feed",
11+
"type": "product",
12+
"update_frequency": "daily",
13+
"url": "https://feeds.acmecorp.com/products.xml"
14+
},
15+
"description": "Synced product catalog from Google Merchant Center"
16+
},
17+
{
18+
"data": {
19+
"catalog_id": "store-inventory",
20+
"feed_format": "custom",
21+
"name": "Store Inventory",
22+
"type": "inventory",
23+
"update_frequency": "hourly",
24+
"url": "https://feeds.acmecorp.com/inventory.json"
25+
},
26+
"description": "Inventory feed for store-level stock data"
27+
},
28+
{
29+
"data": {
30+
"catalog_id": "retail-locations",
31+
"feed_format": "custom",
32+
"name": "Retail Locations",
33+
"type": "store",
34+
"update_frequency": "weekly",
35+
"url": "https://feeds.acmecorp.com/stores.json"
36+
},
37+
"description": "Store locator feed"
38+
},
39+
{
40+
"data": {
41+
"catalog_id": "summer-sale",
42+
"feed_format": "google_merchant_center",
43+
"name": "Summer Sale Promotions",
44+
"type": "promotion",
45+
"update_frequency": "daily",
46+
"url": "https://feeds.acmecorp.com/promotions.json"
47+
},
48+
"description": "Promotional pricing feed"
49+
},
50+
{
51+
"data": {
52+
"items": [
53+
{
54+
"landing_url": "https://acme.com/summer",
55+
"name": "Summer Sale",
56+
"offering_id": "summer-sale"
57+
}
58+
],
59+
"type": "offering"
60+
},
61+
"description": "Inline offering catalog (no sync needed)"
62+
},
63+
{
64+
"data": {
65+
"catalog_id": "gmc-primary",
66+
"ids": [
67+
"SKU-12345",
68+
"SKU-67890"
69+
],
70+
"type": "product"
71+
},
72+
"description": "Reference to a previously synced catalog by ID"
73+
},
74+
{
75+
"data": {
76+
"content_id_type": "gtin",
77+
"conversion_events": [
78+
"purchase",
79+
"add_to_cart"
80+
],
81+
"gtins": [
82+
"00013000006040",
83+
"00013000006057"
84+
],
85+
"type": "product"
86+
},
87+
"description": "Product catalog with GTIN cross-retailer matching and attribution"
88+
},
89+
{
90+
"data": {
91+
"catalog_id": "retail-locations",
92+
"items": [
93+
{
94+
"catchments": [
95+
{
96+
"catchment_id": "walk",
97+
"transport_mode": "walking",
98+
"travel_time": {
99+
"unit": "min",
100+
"value": 10
101+
}
102+
},
103+
{
104+
"catchment_id": "drive",
105+
"transport_mode": "driving",
106+
"travel_time": {
107+
"unit": "min",
108+
"value": 15
109+
}
110+
}
111+
],
112+
"location": {
113+
"lat": 52.3676,
114+
"lng": 4.9041
115+
},
116+
"name": "Amsterdam Flagship",
117+
"store_id": "amsterdam-flagship"
118+
}
119+
],
120+
"name": "Retail Locations",
121+
"type": "store"
122+
},
123+
"description": "Inline store catalog with catchment areas"
124+
}
125+
],
126+
"properties": {
127+
"catalog_id": {
128+
"description": "Buyer's identifier for this catalog. Required when syncing via sync_catalogs. When used in creatives, references a previously synced catalog on the account.",
129+
"type": "string"
130+
},
131+
"category": {
132+
"description": "Filter catalog to items in this category (e.g., 'beverages/soft-drinks', 'chef-positions').",
133+
"type": "string"
134+
},
135+
"content_id_type": {
136+
"$ref": "../enums/content-id-type.json",
137+
"description": "Identifier type that the event's content_ids field should be matched against for items in this catalog. For example, 'gtin' means content_ids values are Global Trade Item Numbers, 'sku' means retailer SKUs. Omit when using a custom identifier scheme not listed in the enum."
138+
},
139+
"conversion_events": {
140+
"description": "Event types that represent conversions for items in this catalog. Declares what events the platform should attribute to catalog items \u2014 e.g., a job catalog converts via submit_application, a product catalog via purchase. The event's content_ids field carries the item IDs that connect back to catalog items. Use content_id_type to declare what identifier type content_ids values represent.",
141+
"items": {
142+
"$ref": "../enums/event-type.json"
143+
},
144+
"minItems": 1,
145+
"type": "array",
146+
"uniqueItems": true
147+
},
148+
"feed_format": {
149+
"$ref": "../enums/feed-format.json",
150+
"description": "Format of the external feed at url. Required when url points to a non-AdCP feed (e.g., Google Merchant Center XML, Meta Product Catalog). Omit for offering-type catalogs where the feed is native AdCP JSON."
151+
},
152+
"gtins": {
153+
"description": "Filter product-type catalogs by GTIN identifiers for cross-retailer catalog matching. Accepts standard GTIN formats (GTIN-8, UPC-A/GTIN-12, EAN-13/GTIN-13, GTIN-14). Only applicable when type is 'product'.",
154+
"items": {
155+
"pattern": "^[0-9]{8,14}$",
156+
"type": "string"
157+
},
158+
"minItems": 1,
159+
"type": "array"
160+
},
161+
"ids": {
162+
"description": "Filter catalog to specific item IDs. For offering-type catalogs, these are offering_id values. For product-type catalogs, these are SKU identifiers.",
163+
"items": {
164+
"type": "string"
165+
},
166+
"minItems": 1,
167+
"type": "array"
168+
},
169+
"items": {
170+
"description": "Inline catalog data. The item schema depends on the catalog type: Offering objects for 'offering', StoreItem for 'store', HotelItem for 'hotel', FlightItem for 'flight', JobItem for 'job', VehicleItem for 'vehicle', RealEstateItem for 'real_estate', EducationItem for 'education', DestinationItem for 'destination', or freeform objects for 'product', 'inventory', and 'promotion'. Mutually exclusive with url \u2014 provide one or the other, not both. Implementations should validate items against the type-specific schema.",
171+
"items": {
172+
"type": "object"
173+
},
174+
"minItems": 1,
175+
"type": "array"
176+
},
177+
"name": {
178+
"description": "Human-readable name for this catalog (e.g., 'Summer Products 2025', 'Amsterdam Store Locations').",
179+
"type": "string"
180+
},
181+
"query": {
182+
"description": "Natural language filter for catalog items (e.g., 'all pasta sauces under $5', 'amsterdam vacancies').",
183+
"type": "string"
184+
},
185+
"tags": {
186+
"description": "Filter catalog to items with these tags. Tags are matched using OR logic \u2014 items matching any tag are included.",
187+
"items": {
188+
"type": "string"
189+
},
190+
"minItems": 1,
191+
"type": "array"
192+
},
193+
"type": {
194+
"$ref": "../enums/catalog-type.json",
195+
"description": "Catalog type. Structural types: 'offering' (AdCP Offering objects), 'product' (ecommerce entries), 'inventory' (stock per location), 'store' (physical locations), 'promotion' (deals and pricing). Vertical types: 'hotel', 'flight', 'job', 'vehicle', 'real_estate', 'education', 'destination' \u2014 each with an industry-specific item schema."
196+
},
197+
"update_frequency": {
198+
"$ref": "../enums/update-frequency.json",
199+
"description": "How often the platform should re-fetch the feed from url. Only applicable when url is provided. Platforms may use this as a hint for polling schedules."
200+
},
201+
"url": {
202+
"description": "URL to an external catalog feed. The platform fetches and resolves items from this URL. For offering-type catalogs, the feed contains an array of Offering objects. For other types, the feed format is determined by feed_format. When omitted with type 'product', the platform uses its synced copy of the brand's product catalog.",
203+
"format": "uri",
204+
"type": "string"
205+
}
206+
},
207+
"required": [
208+
"type"
209+
],
210+
"title": "Catalog",
211+
"type": "object"
212+
}

0 commit comments

Comments
 (0)