diff --git a/schemas/cache/.hashes.json b/schemas/cache/.hashes.json index 99c3288c..1b3a467f 100644 --- a/schemas/cache/.hashes.json +++ b/schemas/cache/.hashes.json @@ -1,89 +1,101 @@ { - "https://adcontextprotocol.org/schemas/latest/index.json": "88911c3318d8fe1f481f157efadbe4e72ea97d7d42c97945bca00f98070c9468", + "https://adcontextprotocol.org/schemas/latest/index.json": "908cdff53fcebff5aaa8ea097f2e6de0e32be3366db8e2fd881c510180ddd9ae", "https://adcontextprotocol.org/schemas/latest/a2ui/component.json": "1ff7f295b03519313584895022e5add7556d275f99333a987301e11c9ccfb3bd", "https://adcontextprotocol.org/schemas/latest/a2ui/surface.json": "7bd19b850a819c6e25f55f37645eeb92a479ece0050ad826fd7f2fbb979ce80c", - "https://adcontextprotocol.org/schemas/latest/account/list-accounts-request.json": "194c59385bbc6e63edbebef814a69bd3167c14a3b172c1f6357327eb92eb4317", - "https://adcontextprotocol.org/schemas/latest/account/list-accounts-response.json": "25b0191d62b9aa14181d70d5bf41014432f76a1f17754548f9f4eedebc43ca49", - "https://adcontextprotocol.org/schemas/latest/account/sync-accounts-request.json": "d09b6f5d354a91201949189b37a353b910c3aa7ecd0469f807c4622d6bacf069", - "https://adcontextprotocol.org/schemas/latest/account/sync-accounts-response.json": "3ee5ff9697afa84830990d244209e31ba91ca154a9922e74c991112c4181b176", + "https://adcontextprotocol.org/schemas/latest/account/get-account-financials-request.json": "626d88e9a0e54ccba8b015527a585c8df741f1dc103aff0183cea217b84b80f5", + "https://adcontextprotocol.org/schemas/latest/account/get-account-financials-response.json": "00c962d9021d10191f3860df25ae2dd73730ac3c83d790e997478b0dda2d9a01", + "https://adcontextprotocol.org/schemas/latest/account/list-accounts-request.json": "c7e8b8bfb2a77e7080c32971b3aeed06070f8b0c3eb13ec4dcecacc712184794", + "https://adcontextprotocol.org/schemas/latest/account/list-accounts-response.json": "a1bbf036853fb39044d36f4bfb62b54b3da93b7bd53aaf2110297dd7fd978061", + "https://adcontextprotocol.org/schemas/latest/account/report-usage-request.json": "90b14d6488a74a3a82637d2b74c4f4d4367a12ca61f59335729982e29956a213", + "https://adcontextprotocol.org/schemas/latest/account/report-usage-response.json": "e2731baaa0456c68f2fab3c9545412ac7a28638b87a72025fc51a00443498d6d", + "https://adcontextprotocol.org/schemas/latest/account/sync-accounts-request.json": "dca6a8e052ce4aa06ccdac09d8db6c98aa831bdae231564ef3254d937601ff17", + "https://adcontextprotocol.org/schemas/latest/account/sync-accounts-response.json": "00ed54992963b40c3231dfa00f0e8313da8d652e3c5812ad676d3b197b1b990d", "https://adcontextprotocol.org/schemas/latest/adagents.json": "c9608e07f49ccedc7420d1f0d205a92dd78cbfed662de606d458cb3e5c46efea", "https://adcontextprotocol.org/schemas/latest/brand.json": "0cb68a8f1683c0af3e28fb1e48f602832e78321d1c575504be62997991003dfc", "https://adcontextprotocol.org/schemas/latest/content-standards/artifact-webhook-payload.json": "a5ddff12ff1d6316636919bc87359980512e63ff688d4b742628ab06b91b537b", - "https://adcontextprotocol.org/schemas/latest/content-standards/artifact.json": "b499571575b8e0f11b5c21dd32189fd19191d32a795cff225165fdfd0092917b", + "https://adcontextprotocol.org/schemas/latest/content-standards/artifact.json": "e2c80b3b8370caad8277b60f5285349e630f275f9f63f0ac02d019fbe84056d9", "https://adcontextprotocol.org/schemas/latest/content-standards/calibrate-content-request.json": "fee8b02f136fdbcbe9ca35879d71f19cf37349de9b047e1c6e7e65b91b23bc3c", - "https://adcontextprotocol.org/schemas/latest/content-standards/calibrate-content-response.json": "55b76ae2eb728abc8b5dd9d5d2f3af25a4dc6c31c06d8a76db5d4544e6001ecd", - "https://adcontextprotocol.org/schemas/latest/content-standards/content-standards.json": "05c7e4528eab11b1a010d46fb6c13bb54d40dd088ed202d7d74bf55fb40f1465", + "https://adcontextprotocol.org/schemas/latest/content-standards/calibrate-content-response.json": "e85fd0507d8e98bc37bae1610e715b03cf8e8361aaded1004004c21e47b5b50b", + "https://adcontextprotocol.org/schemas/latest/content-standards/content-standards.json": "cbaac1a84ca1d83ae158e6f465398e7e55baf8078e234785ed48462f245ce467", "https://adcontextprotocol.org/schemas/latest/content-standards/create-content-standards-request.json": "1adc8e7387b215b22e822fcbd13fe4775eb4ac340cf6200de77afaae41a4889d", - "https://adcontextprotocol.org/schemas/latest/content-standards/create-content-standards-response.json": "9c5cf7b12680c14ce13542d0c032f7b5135a0546a917147aac8633bd43618ec5", + "https://adcontextprotocol.org/schemas/latest/content-standards/create-content-standards-response.json": "944ca233a74ddd64528c117fcf2fb9a673ab0eb153487b10112d2f3bf77723b1", "https://adcontextprotocol.org/schemas/latest/content-standards/get-content-standards-request.json": "abe28cf4158bfa4f4333734639e62fd4d3057ab9fbc8eed0e121e51ac6962004", - "https://adcontextprotocol.org/schemas/latest/content-standards/get-content-standards-response.json": "2b857288ce95461fd5e278fe27d6a21dbb06c442ee2a8f41db2530e547280991", - "https://adcontextprotocol.org/schemas/latest/content-standards/get-media-buy-artifacts-request.json": "515787e0412da0b06924a11562b5428742b544af4ab585ba057f49c1dddefb16", - "https://adcontextprotocol.org/schemas/latest/content-standards/get-media-buy-artifacts-response.json": "b03bcecc5c2354ec1678cd61c71ad38ece2abb0f52e2047ae86e599738c9cf43", + "https://adcontextprotocol.org/schemas/latest/content-standards/get-content-standards-response.json": "e9f0cf6325a64f2f072c3f11ec090d6c68dd9f342f957473d080823054fffcec", + "https://adcontextprotocol.org/schemas/latest/content-standards/get-media-buy-artifacts-request.json": "a6bd48afd93ba96119f5858269f73f0fb9e610f20c4675b5916927da383d6eb8", + "https://adcontextprotocol.org/schemas/latest/content-standards/get-media-buy-artifacts-response.json": "20daa7e777e541621c3fec9247351f000a65e5717da6457a37e8eb78676d239b", "https://adcontextprotocol.org/schemas/latest/content-standards/list-content-standards-request.json": "9fab6d60550d4e229ac1acc69658f8718cf34a9afb14f5b7c3f22076432bec7c", - "https://adcontextprotocol.org/schemas/latest/content-standards/list-content-standards-response.json": "2b24793b3196d2da1896d10e5298fcce43c72486859ae47fe0cd6b0ebedebf3d", + "https://adcontextprotocol.org/schemas/latest/content-standards/list-content-standards-response.json": "37a724528375e05e5fccb3140f7389844d9ea4903374785eb2c5eab730aad716", "https://adcontextprotocol.org/schemas/latest/content-standards/update-content-standards-request.json": "934163efa243f33bded9b18b458e6515161c5eb65ea82600713b1c319c8a4402", - "https://adcontextprotocol.org/schemas/latest/content-standards/update-content-standards-response.json": "68b2307f5a152df438f8b406873292947d477cd1d8e392a074ac5b7f61dfd190", + "https://adcontextprotocol.org/schemas/latest/content-standards/update-content-standards-response.json": "96a592c1f40de17919263f510240bb384e1fc8b2f1e0528a551ae15f6f35e28b", "https://adcontextprotocol.org/schemas/latest/content-standards/validate-content-delivery-request.json": "560e1251702f3c09ea5886ff730fb4c88d53a2e6a8b4c9804bfa60ac9ad5a800", - "https://adcontextprotocol.org/schemas/latest/content-standards/validate-content-delivery-response.json": "a3223d796114054b90a67ebff460286a5231f956cfbbfae2398fef518dfe3b55", - "https://adcontextprotocol.org/schemas/latest/core/account.json": "dbafbe4df44c4b1f073adab9fa370faa223b957c8653c5fc924c5b50cd1484b7", + "https://adcontextprotocol.org/schemas/latest/content-standards/validate-content-delivery-response.json": "fed40f3e68c3c71a6735fd63519c72c3e37f2f421640179fede03ce67d74cb2e", + "https://adcontextprotocol.org/schemas/latest/core/account-ref.json": "65d0a5636a82ef83c32a7ce735378e24897c5fc6c28981a9ba346206c9589ecd", + "https://adcontextprotocol.org/schemas/latest/core/account.json": "74c4dd9469d66e22116f150d69470b8a62cac0bd14eebb7e923c15df9d5807c3", "https://adcontextprotocol.org/schemas/latest/core/activation-key.json": "a31fb82057c50037575304641bcb32999239a762d93c1b3f1c5f535678c898a6", - "https://adcontextprotocol.org/schemas/latest/core/assets/audio-asset.json": "3b90c8dc0c90abede1685c2cef9ea7516db4f9297800a0df648344bd4cef5399", - "https://adcontextprotocol.org/schemas/latest/core/assets/css-asset.json": "b37b137ff038e3b147d52fdcc221e6961fe3f3caeb62b4e3f2356cb448ed7500", - "https://adcontextprotocol.org/schemas/latest/core/assets/daast-asset.json": "d865304f3f316c4eb9e564d457b8778fac36806953beef61b8c7b6527289e142", - "https://adcontextprotocol.org/schemas/latest/core/assets/html-asset.json": "fb99f50038efecf5347542dd97e2a97ebb8d9af2fb5a16ea2fb27a8061295f42", - "https://adcontextprotocol.org/schemas/latest/core/assets/image-asset.json": "41619f4752377b258f3dac79597348f1cfab337420aa60160e167aa2cc6ea1a8", - "https://adcontextprotocol.org/schemas/latest/core/assets/javascript-asset.json": "bcef50ba306604f81bbeddef1023b598124f70fa6b0be11cb3702364d1ed247e", + "https://adcontextprotocol.org/schemas/latest/core/assets/audio-asset.json": "41fc9ff6f4440f3043ff059cf552ade967ac491798b5fc223129bfc854f3f98a", + "https://adcontextprotocol.org/schemas/latest/core/assets/brief-asset.json": "f65afd01f8ad48a93220c7b5875f27b30a292fb7e8f3bc94db6871496d249cd4", + "https://adcontextprotocol.org/schemas/latest/core/assets/catalog-asset.json": "066252e944cf2af0d177613ce4d239510f6d7e135419814c6618fcbbd679e7f6", + "https://adcontextprotocol.org/schemas/latest/core/assets/css-asset.json": "7e3b3f8d7403c16bdd1bd3db248ea32fe491703d5bd8d3ad03c0200fa45c9401", + "https://adcontextprotocol.org/schemas/latest/core/assets/daast-asset.json": "f5d55ba0e5cf98ed27e4061b39ff932d6ee9e4e9e248ab0019f5b4c5182dca30", + "https://adcontextprotocol.org/schemas/latest/core/assets/html-asset.json": "ff4bd60a492e43b853897b1d0ccfee9d6ed626408d3f0076c10b671638b260e2", + "https://adcontextprotocol.org/schemas/latest/core/assets/image-asset.json": "47507cf4800fb7b061689a0f8cc35c4099cb132efc4397c6ab9dfa3ea21fd70b", + "https://adcontextprotocol.org/schemas/latest/core/assets/javascript-asset.json": "c413873f9e0badbbde6aa3eaf844e95b006d1f71a0e323f827c080dc17720591", "https://adcontextprotocol.org/schemas/latest/core/assets/markdown-asset.json": "6937d04fa26eb032e80b895db628cb8a1d6f38545a1cc6fd8dfda2479511319c", - "https://adcontextprotocol.org/schemas/latest/core/assets/text-asset.json": "b6a9bf1dd18971ff368d4d2d9c3796705e83c82898bb8672eda6a684149f42d4", - "https://adcontextprotocol.org/schemas/latest/core/assets/url-asset.json": "96ce5efde3ef98bba10d8ea9a43d8e778229b6bc1b44e5348e8380499e192fdc", - "https://adcontextprotocol.org/schemas/latest/core/assets/vast-asset.json": "8b55d5606f31aabfc0dc789f194f8854e9f917c244c3b8077eaebd1a26a111e7", - "https://adcontextprotocol.org/schemas/latest/core/assets/video-asset.json": "ade4514c505fba37670d51b4ecbddb1fe97c3f1e43ea9c59dbcb8b7602dd1237", - "https://adcontextprotocol.org/schemas/latest/core/assets/webhook-asset.json": "80cbe14067f4ba0155b32e089cfac4446230f64f9e4c189adafea97f1950fa7c", + "https://adcontextprotocol.org/schemas/latest/core/assets/text-asset.json": "90e0dfdaf8d20e4649288427cabb078b07d1101cbfa16e86c11b9c0ab3ba314a", + "https://adcontextprotocol.org/schemas/latest/core/assets/url-asset.json": "66641f372261c07991a88b33c8cf33cecb85aeea255c43dd2b3c1f7cd632cae7", + "https://adcontextprotocol.org/schemas/latest/core/assets/vast-asset.json": "5084de0799ebe37fe3198b3e3375f65f505dea332998623c003848f703b3e149", + "https://adcontextprotocol.org/schemas/latest/core/assets/video-asset.json": "affd40dcc94166e2a0dc9d1580dc66b6bff55e543a5d37074f0932c1865f27d4", + "https://adcontextprotocol.org/schemas/latest/core/assets/webhook-asset.json": "c9151f1088f41d989972f6a0cd2c093768b59075c3b99aa42d3c077260b84a8f", "https://adcontextprotocol.org/schemas/latest/core/async-response-data.json": "6a1d5bbd74073025670c834804f594ce6c15070c95b181af327dc311cfdad0be", - "https://adcontextprotocol.org/schemas/latest/core/attribution-window.json": "cb09c3c758fbe16cd606f88fa8218105bbe03b02a659ca5a526799fc454ab7d9", - "https://adcontextprotocol.org/schemas/latest/core/audience-member.json": "c9915205b547039c423c61b94d5755e511b1a12870906e722df7b66e6bf06b35", + "https://adcontextprotocol.org/schemas/latest/core/attribution-window.json": "3a7a7196cb0dba42ae7d9591ed645b91c748ac499573ab893bcb9fd26e5170cc", + "https://adcontextprotocol.org/schemas/latest/core/audience-member.json": "c2abc6c34d0ec8e2415281538bb81f95283b9ff658bfc97d71612e1bbee215e6", "https://adcontextprotocol.org/schemas/latest/core/brand-id.json": "dbfc3c2aed4d16ecbba3204200948437a2bb3c0b68c0117c3a8c93f3c0700d25", - "https://adcontextprotocol.org/schemas/latest/core/brand-ref.json": "b97b4c6a2c337078893cf4da6859e3ad4e14a950f643e306d9b308de4bc60ca3", - "https://adcontextprotocol.org/schemas/latest/core/catalog.json": "bad3a4c7e944bd4d9827c97a643217dcd3fadbe71a8401c983f52f2839a99113", - "https://adcontextprotocol.org/schemas/latest/core/catchment.json": "aeba7b29541668df1028013847fb8336a58292d441c483dd9f9b5b744fcb0490", + "https://adcontextprotocol.org/schemas/latest/core/brand-ref.json": "b3e17f59857f11ed792b6bdbfb88e907fed2bbcab3a99bcac748b7e2887f146b", + "https://adcontextprotocol.org/schemas/latest/core/catalog-field-mapping.json": "a37d2285f781ef241e763822a1b1f25e2549b0207264fcdfc0d3a10fe3a6e754", + "https://adcontextprotocol.org/schemas/latest/core/catalog.json": "0949a9d7e112fa5e5901348726091c956f701eddafa76cda4088bde4e5584898", + "https://adcontextprotocol.org/schemas/latest/core/catchment.json": "447f38d36e0924a282c1e67392efcb67c19a6c7bed0b5bb48c3f1ad1f6712788", "https://adcontextprotocol.org/schemas/latest/core/context.json": "b5b5de9482d662c1eacfcfdfa53de672a047664e295940cb204f2d822d5eeb31", - "https://adcontextprotocol.org/schemas/latest/core/creative-asset.json": "1f0f431d11cdae423835734ba6eb7c002da47a8d8a480c76f84ef3814f0c1d68", - "https://adcontextprotocol.org/schemas/latest/core/creative-assignment.json": "b62b9db75d306abe2e5f097aac1caaaeb214c0b22d37f58d63ba3f5c428f4f10", - "https://adcontextprotocol.org/schemas/latest/core/creative-brief-ref.json": "f664328da2c35e17c4ea72343ff471f51d6905f9414ae798ed186a7576f88d9c", - "https://adcontextprotocol.org/schemas/latest/core/creative-brief.json": "bbf2b432a7e8c63dd2649a6878d72753a219965906fd043c453acca691c49827", - "https://adcontextprotocol.org/schemas/latest/core/creative-filters.json": "62ce7508144cd1bf6c5e00fb618ceb98f2d4e30fd93c869cd661b530e81a9f8a", - "https://adcontextprotocol.org/schemas/latest/core/creative-manifest.json": "bf3c74746b737d94abb760ff95d02964ec2124a6ab18a7f600a07d9bb85899a7", - "https://adcontextprotocol.org/schemas/latest/core/creative-policy.json": "c63d0489e8d7423b49471b3ceeae3df6716e5313a842bdf881dfa4e1f3516e6c", + "https://adcontextprotocol.org/schemas/latest/core/creative-asset.json": "571fa2d526d36b30e668f4aece96cd27a5ef7f9fae316a1893f43138cd68e1d6", + "https://adcontextprotocol.org/schemas/latest/core/creative-assignment.json": "c0247fe073290149567d26c95df3cc6c83a63574dedbb3a993424568dc35774e", + "https://adcontextprotocol.org/schemas/latest/core/creative-brief.json": "82175cc9614ba8928a138672b7aac79d4e05a8173ba0a58108307454a93679f8", + "https://adcontextprotocol.org/schemas/latest/core/creative-filters.json": "6eb9bb30a8e6ace5c9e808c904a592225e96fb49d6206ffc6227ebb0786139e9", + "https://adcontextprotocol.org/schemas/latest/core/creative-manifest.json": "8767b52b7a08bc16360567313279ff75d5184786860b1e9073fab97584c61df4", + "https://adcontextprotocol.org/schemas/latest/core/creative-policy.json": "66ab4eba9d63c7ad2543298166efe7007dddf40a7fcf8fd8f590cb075ce5a6ce", "https://adcontextprotocol.org/schemas/latest/core/creative-variant.json": "f6833767d7bd9e617de3aa6888b3997b6fb8518085bc335d201450b646f9055c", "https://adcontextprotocol.org/schemas/latest/core/data-provider-signal-selector.json": "58b09cd1288007e46d7ed45bb55d1c8743a9e358236a16586940311f5fd5fdd9", + "https://adcontextprotocol.org/schemas/latest/core/date-range.json": "a61d68c5f6bc6b219cdeece5d97ea9bb3614556289cbc5bbbcf6c0f109993bb0", + "https://adcontextprotocol.org/schemas/latest/core/datetime-range.json": "31851233d56cc3a67af200ec07dd42574b7b0608afef0b5aa4ee95e9e1933887", "https://adcontextprotocol.org/schemas/latest/core/daypart-target.json": "63d103c751558a96ff308b0a54ecb3c4464880f81577cd87ab910713ec7d3156", "https://adcontextprotocol.org/schemas/latest/core/delivery-forecast.json": "c6ac7da6a25ec81be6f8b09d466bc528a8c3ef207dd7c6f8ec2cceed5d8d8987", - "https://adcontextprotocol.org/schemas/latest/core/delivery-metrics.json": "3b271580ae8432ee21c2a2e909fff568cd424e5ed86fe0c1da667964c6f0e9c8", + "https://adcontextprotocol.org/schemas/latest/core/delivery-metrics.json": "f67364df3bc8cc89a0558b4b752573040cb9a32b677919779b3d9bdacf194477", "https://adcontextprotocol.org/schemas/latest/core/deployment.json": "5769c02297e584f761a55301732621e22f8166e9806a1933cb842c1be9902914", - "https://adcontextprotocol.org/schemas/latest/core/destination-item.json": "91caabdca2577e664bd5218f889a814e3299650e482ecbc09cb6f26c8e546db6", + "https://adcontextprotocol.org/schemas/latest/core/destination-item.json": "5b54631d530f858fb8116a3fd34a56a43be10860c821f6bfba459d81eebd8d48", "https://adcontextprotocol.org/schemas/latest/core/destination.json": "007880839e1d556972365ba1cd7076d081bbd6e6b9b0ba47147fc2748ce60d35", - "https://adcontextprotocol.org/schemas/latest/core/education-item.json": "1dfa39dd292f654f55fa86c25767adee79d9430c854118edf468e761a1202049", - "https://adcontextprotocol.org/schemas/latest/core/error.json": "97dadd80893ba1c3d371e7b3fefe33941e3b8e660e91d0e8e1517c5144851b1d", + "https://adcontextprotocol.org/schemas/latest/core/duration.json": "0ecc796d27fbdb6a56cfbe579716fdb667d0b16139e9a48cf3dafe451320d4e2", + "https://adcontextprotocol.org/schemas/latest/core/education-item.json": "00a583b3bd5e9ef8f81ae0ee7f79aec6b40e84b2e3ecf2528828d713d4783b42", + "https://adcontextprotocol.org/schemas/latest/core/error.json": "148e6da6e3ec332d224ace5850f8288e47d73ed1f17e4407deb07aa8414f8a46", "https://adcontextprotocol.org/schemas/latest/core/event-custom-data.json": "6270adde29c7ed39cfba921cae6d793e6bfcfa7d1da71409bd490a394305c676", "https://adcontextprotocol.org/schemas/latest/core/event.json": "e0c7caa7e23cc8ae493883caf52c9c012e5124acb295438158639b71e18a0b2b", "https://adcontextprotocol.org/schemas/latest/core/ext.json": "0dc59d0cf5bcf97f9ce9ba635f977ec65b982e853cc0b05decb034cfb642f4b5", - "https://adcontextprotocol.org/schemas/latest/core/flight-item.json": "d180e5a4dc3350e38e2b6311068db380a1fbd2f31f001c2c0c4fc57de979cf9f", - "https://adcontextprotocol.org/schemas/latest/core/forecast-point.json": "a144ec1c7223c0f724baf260f1d47e43f909c1894c6319d4b5fa5e295a8b8dea", + "https://adcontextprotocol.org/schemas/latest/core/flight-item.json": "5a72496482bcaab74e99593d24d08f99dab778baec150e6b23998403c9dfef40", + "https://adcontextprotocol.org/schemas/latest/core/forecast-point.json": "d65508b755872e625c99ad24913df72efc3c92ac1827f90775d652bf63b40631", "https://adcontextprotocol.org/schemas/latest/core/forecast-range.json": "831510663009ed73a328a015398130603a934031c963d973d70783bfa8beb44d", "https://adcontextprotocol.org/schemas/latest/core/format-id.json": "529433d6d5ecac7514d85a0bdf80caf0a69a3c5c0f77a14932441a954a8ada15", - "https://adcontextprotocol.org/schemas/latest/core/format.json": "167e4ec7898e226566a7188b6afc1b95f455e20f6622ad5f24ef8fb12e4e134f", - "https://adcontextprotocol.org/schemas/latest/core/frequency-cap.json": "78e7d100a8f99c59d8190f487a868bac804a25a3797c06b8e668e782c0fe0dfb", - "https://adcontextprotocol.org/schemas/latest/core/hotel-item.json": "e777858324135949440dfb4a304b6f86ccac36c4015f4c3fe4a91223384a391b", + "https://adcontextprotocol.org/schemas/latest/core/format.json": "c640101aa284e5fa9adec7a76b266e45d7b84b2b8cdaaee03d88832b6cdc237c", + "https://adcontextprotocol.org/schemas/latest/core/frequency-cap.json": "8ff591538883beb8b687e5a0f77d762369e0c2e5e0837f917add5009f1cdec0a", + "https://adcontextprotocol.org/schemas/latest/core/geo-breakdown-support.json": "f0ea4566dafc3893f249fa5dd3d4e3794ee747a8cd4c34e4a5f70362927f4ee8", + "https://adcontextprotocol.org/schemas/latest/core/hotel-item.json": "9a935aa2a07362c4d9fa481e79e68b61ce6f0bd4f07fb0462278706e89d891cb", "https://adcontextprotocol.org/schemas/latest/core/identifier.json": "da78a993f66120ab06fbb6e4801a3806db72c6e5ad990c9565965b8c8b625fca", - "https://adcontextprotocol.org/schemas/latest/core/job-item.json": "6e56b499b6b63763594af8a60cf014921a96a34a5240a942bf8cdd1b35e6836a", - "https://adcontextprotocol.org/schemas/latest/core/mcp-webhook-payload.json": "1dae520cb0c0564f4c647a5fba4cb8383897f28dcf72a2d046986684255f099e", - "https://adcontextprotocol.org/schemas/latest/core/measurement.json": "069685e5f69b28598a35e6887a289f82ea830c1d9c0a8c99e621d3c1a96c72fd", + "https://adcontextprotocol.org/schemas/latest/core/job-item.json": "d3ea7fd474b3eed68335b2eef3152a531343dc42a175a3b7fa7d8cb66c0577d6", + "https://adcontextprotocol.org/schemas/latest/core/mcp-webhook-payload.json": "93fc71777533eded431312538c5291f199a73099201a5ee0ce6f9eb1ec669d48", "https://adcontextprotocol.org/schemas/latest/core/media-buy-features.json": "57d0728159ee7558fb15a33f9027997f7d2738408f3c87a5954922b5276521be", - "https://adcontextprotocol.org/schemas/latest/core/media-buy.json": "d083007c8b8511e44593e98073d8400c517be7bc58a829c9a2da6324cbae2022", + "https://adcontextprotocol.org/schemas/latest/core/media-buy.json": "a59b74ab894b7a750665fbc3eca690046b04ec0b7839fca6eb4c04b43ed07b78", "https://adcontextprotocol.org/schemas/latest/core/offering-asset-group.json": "78951ed5a5b5bdaa0333d1bdb80ba7a5349be7059ce740a784cfe82e9e28dec3", "https://adcontextprotocol.org/schemas/latest/core/offering.json": "4f5bda01b6013d1df56ca8d161b9230c13759f6ec3c70ad44e145855b8cc29d3", - "https://adcontextprotocol.org/schemas/latest/core/optimization-goal.json": "b07f81fda0a47cb52d6842859e240fbfadd4a41b31316cac0b85163624885401", - "https://adcontextprotocol.org/schemas/latest/core/package.json": "5d0b5fc8301328c0c663e9840efc3fb296056a6bfaf8077c6a089ffc3ac7b508", + "https://adcontextprotocol.org/schemas/latest/core/optimization-goal.json": "c07603104cc7d76036e38d8a02f2a8b760669cce3cca2760da0d60e174a0ab38", + "https://adcontextprotocol.org/schemas/latest/core/outcome-measurement.json": "32483c8bcd0ac0a5aa7d53797457f45b931e5d80794c81eacebd16afe0ab36cd", + "https://adcontextprotocol.org/schemas/latest/core/overlay.json": "c13c22f6279f1b380397dd4df151c66d7ebd09d90dfd310a319c71899e154e15", + "https://adcontextprotocol.org/schemas/latest/core/package.json": "16e518006be804b465271edce3ee10e597f345c458668a720f3f393e4eb08108", "https://adcontextprotocol.org/schemas/latest/core/pagination-request.json": "3cabe1c0ed60e531149578901518e9091e2c2c4345741cc8de37245def768006", "https://adcontextprotocol.org/schemas/latest/core/pagination-response.json": "0279f24bdb2cfefd15d0f4c9faa4b5a59c39f1342ed0edfb8e66d8865b2395b6", "https://adcontextprotocol.org/schemas/latest/core/performance-feedback.json": "63dec657c26dd2702d9051d151d428b97ce8ad427fea8571c571a8eb8f53d99c", @@ -92,22 +104,24 @@ "https://adcontextprotocol.org/schemas/latest/core/pricing-option.json": "ffd419298a94a388ead73e5ca27fb981ed2865125c1b3a8f05dbedc0623b0590", "https://adcontextprotocol.org/schemas/latest/core/product-allocation.json": "839db85911a6223fa1ddbabae93385de4dd55869800f2b71742dfeaeed676ebc", "https://adcontextprotocol.org/schemas/latest/core/product-filters.json": "8fb007b7e1f9adacd7fda1bf85b39af8b5e8524f15f4a170211a607e3571aa0c", - "https://adcontextprotocol.org/schemas/latest/core/product.json": "610741ce3aee286183ad02c1ee64084047102349640ef0eaf5986671b48d0ade", + "https://adcontextprotocol.org/schemas/latest/core/product.json": "a265e3544f276a4be5c4f600c666efc8e2a4ccb04da86aede1bedb67ffaba584", "https://adcontextprotocol.org/schemas/latest/core/property-id.json": "16ff353402c03691a7290a0f7578a901b63a0f7e83567b8903c5c6f89e1fb6f0", "https://adcontextprotocol.org/schemas/latest/core/property-list-ref.json": "50c908159f9d02bff2a3330ac1bdbe2a46c47649a3efbc0a8eb9f8118b3b70bc", "https://adcontextprotocol.org/schemas/latest/core/property-tag.json": "6f2c35324ca6c5533a2c3d1a5837360c813a0905be68b11da7a46965ec37d59c", "https://adcontextprotocol.org/schemas/latest/core/property.json": "f2057d364045fb833b9884a0902abdf1570719d4ab3bafd5649eb7b134d4911d", "https://adcontextprotocol.org/schemas/latest/core/proposal.json": "855cd118a5b0aac6ed8f1628be49757348b0374c17596459aae8262b0dc8b4dd", "https://adcontextprotocol.org/schemas/latest/core/protocol-envelope.json": "abaef42600786d78e1dfdc4986eb3ca7c3cd7f4955a2b8e9bfe27a4d9a58c3f0", + "https://adcontextprotocol.org/schemas/latest/core/provenance.json": "62228a49e1b50b9829c1187cf5a2fed533a9c5471927df7c22b0687fd084c1cd", "https://adcontextprotocol.org/schemas/latest/core/publisher-property-selector.json": "2763ecac8effc1e27bf69d231968f25bad5ee2bb723ad1bf9c3da125d6b64099", "https://adcontextprotocol.org/schemas/latest/core/push-notification-config.json": "64ed4db710d5a5346d6935e238d28ffaa4374dcff9d08e5d4283d4786dfe6de5", - "https://adcontextprotocol.org/schemas/latest/core/real-estate-item.json": "ba4baca2c22b6ce4cb214de0fbb7cbde8c422f024a7ac0024c1c116c180d9380", + "https://adcontextprotocol.org/schemas/latest/core/real-estate-item.json": "ba4f928b5363cc443bded67b62e6229408a051a246a842a777eacfd09c004f0d", "https://adcontextprotocol.org/schemas/latest/core/reference-asset.json": "76e1bed153f7bfcd1473401c7074a1679935491de8af1aa84530e44af48baecb", - "https://adcontextprotocol.org/schemas/latest/core/reporting-capabilities.json": "fca3c737c6214cd89fe95cd53acd7622e6f2951185ddf1c6fb869b1f23a245a6", + "https://adcontextprotocol.org/schemas/latest/core/reporting-capabilities.json": "61cd2822d80fca977b809707d81db3db7aa106d4b4af4bfbf06289d2df775dfa", "https://adcontextprotocol.org/schemas/latest/core/reporting-webhook.json": "90e90d454334590670cfb881b14be00dd2a7fc134b0cc4494c79c6848fc2db1d", "https://adcontextprotocol.org/schemas/latest/core/requirements/asset-requirements.json": "07c611e72f87aed09c5d20b9170f65280b46d01220b00e6d5d1f31943bfc8474", "https://adcontextprotocol.org/schemas/latest/core/requirements/audio-asset-requirements.json": "bd73bc4f72429d9c9d8cf05ad6f97f5a345d0a43f9b791df1789a54a22f84be6", - "https://adcontextprotocol.org/schemas/latest/core/requirements/catalog-requirements.json": "03cbb6cdd629280fa47934c7b3458c279bf338cb624545572315b12948e80b4e", + "https://adcontextprotocol.org/schemas/latest/core/requirements/catalog-field-binding.json": "d0d66de8474d2fda9d04954bce5186d6637a62d94580da5ff1db2f57af204a50", + "https://adcontextprotocol.org/schemas/latest/core/requirements/catalog-requirements.json": "967b61c9f338b31fbfb962fe8afb65d736c2fe630259d002f0bb04b630dc5a77", "https://adcontextprotocol.org/schemas/latest/core/requirements/css-asset-requirements.json": "ec0722f9068095a8bdbed0178d84265875f9f52d5aebd957462fb02fd623c3fc", "https://adcontextprotocol.org/schemas/latest/core/requirements/daast-asset-requirements.json": "d93404d9be0cf5b1bb8064beb674841394cfd6554d24efbc60ac1ad1cafac73c", "https://adcontextprotocol.org/schemas/latest/core/requirements/html-asset-requirements.json": "0148d6d6ad4d0f23bb0db6e987642a4aaa0d12574d3decf653338935e80a6be7", @@ -122,31 +136,34 @@ "https://adcontextprotocol.org/schemas/latest/core/requirements/webhook-asset-requirements.json": "0b6a9d602895cf6d12f8d70e4fd27d9914288ce09901149cc61dd3fbd3b347f1", "https://adcontextprotocol.org/schemas/latest/core/response.json": "232e6fc35a8dec7e7ad0743cd9be807cd7143e32eeb162f830ab692347744967", "https://adcontextprotocol.org/schemas/latest/core/signal-definition.json": "a44bce7c48946cb1aff0431170b940796afd36705c7bb0cf06f609e4b8705fac", - "https://adcontextprotocol.org/schemas/latest/core/signal-filters.json": "31748069e4127c516497cd2721f551a326e8f93b39c24dce2e393e677f90c044", + "https://adcontextprotocol.org/schemas/latest/core/signal-filters.json": "c250779da0fb5e40a8a78f41ad1b4a64b7704b81e03b64bd304442cf2496452e", "https://adcontextprotocol.org/schemas/latest/core/signal-id.json": "f5bfd276c5eb7b3ce334e6681ebf67642ae28acf10d7527f0ee142a9c41a59dc", + "https://adcontextprotocol.org/schemas/latest/core/signal-pricing-option.json": "f570fe67ee418d79187b45c08f1fb659bbac76fce4303736a3fdcca16b742ec6", + "https://adcontextprotocol.org/schemas/latest/core/signal-pricing.json": "20df35e95f500fb997e50db55048bf7e07f383e2c95791d5b9271f5bb58a97ce", "https://adcontextprotocol.org/schemas/latest/core/signal-targeting.json": "e80dcf066fe206808029174d0460139adc4c6ab9da70b82d0a29730efdab2a90", "https://adcontextprotocol.org/schemas/latest/core/start-timing.json": "8e07e0469f434f3d3ba998e2b1fe58fc3dd097915e2d5e0c547cadfd5a469158", "https://adcontextprotocol.org/schemas/latest/core/store-item.json": "54b1a5bc1eb10921c948cec80ef6af6f0d577ca787aa900c9fab6435d75c3381", "https://adcontextprotocol.org/schemas/latest/core/sub-asset.json": "c0293416fd0275afd5ef59bd68675ff8ea37e4d4ba8f3a13e375711529a6399a", - "https://adcontextprotocol.org/schemas/latest/core/targeting.json": "54f77493cfed2a3396852ad2b9d926c18a1fea4ec58324a78610a3f0ccf51108", + "https://adcontextprotocol.org/schemas/latest/core/targeting.json": "70d4a8d65a3b64e746257d8760686e4ad143285065b32cfc2d95fb9a17a2d901", "https://adcontextprotocol.org/schemas/latest/core/user-match.json": "fe94aac841014b1f203583b0b4b4c736046867b4ff392ce9bd5b8f4625aaa452", - "https://adcontextprotocol.org/schemas/latest/core/vehicle-item.json": "7f817c04c8f718cb7d7f1e8753f269102bbceb6a9eaf02e4e39715a508fed282", - "https://adcontextprotocol.org/schemas/latest/creative/asset-types/index.json": "2b5d88f8843e5dd469c3ae760193a191c3aefb47359b27daa871337427e7d0b6", + "https://adcontextprotocol.org/schemas/latest/core/vehicle-item.json": "7ccf5ba20df8853a28c27c1c3ae9790bd2051fdea8d35c1691ef8b6561c031a8", + "https://adcontextprotocol.org/schemas/latest/creative/asset-types/index.json": "775ecc3f882489adafdf27deff9c360440687c694a58c4a49ee1f38cd83e0f04", "https://adcontextprotocol.org/schemas/latest/creative/creative-feature-result.json": "e9a6e59fa298cbe145d2a9f72eaa498728e88b5bfaa9f5e3d8789204f11da98d", - "https://adcontextprotocol.org/schemas/latest/creative/get-creative-delivery-request.json": "1dfca425f543af3ad29a997725812a605ed10b2fc3b8511fae9bc88e44c40b96", + "https://adcontextprotocol.org/schemas/latest/creative/get-creative-delivery-request.json": "7c9740f1e394cdefdc0a8b3d89464ebce101e96f9ac19194d497e5b50e8ef400", "https://adcontextprotocol.org/schemas/latest/creative/get-creative-delivery-response.json": "ac4acc3a240ccf1197b54bf7aef076366222103b9fc3031fe8022633ce994db3", - "https://adcontextprotocol.org/schemas/latest/creative/get-creative-features-request.json": "66b8a80209f6454ae02b4aeb2ec22ce3266fea85bb8d2728364963d976f4b9db", - "https://adcontextprotocol.org/schemas/latest/creative/get-creative-features-response.json": "91a704047c25cba957488e6ae2ab9b19034c71a1df56430fa7b03867b8be134e", + "https://adcontextprotocol.org/schemas/latest/creative/get-creative-features-request.json": "24a37b9fbde7c47a599608b1a184bd7ae0d07613fdfd7af4be11dd5e79f76d10", + "https://adcontextprotocol.org/schemas/latest/creative/get-creative-features-response.json": "e3e3c14651b52e3aa88a4f381d0a1dc0e635b9cb4053d7597cd49d334ecf9a9f", "https://adcontextprotocol.org/schemas/latest/creative/list-creative-formats-request.json": "0f6cae66d4236db5914dd8a20d85137ac1d7b3ddcd3551358a8a4811377e9346", "https://adcontextprotocol.org/schemas/latest/creative/list-creative-formats-response.json": "3b5a07f923bcc243fc81c5f4725627b574b0d534de3b176fd972c2349364428d", - "https://adcontextprotocol.org/schemas/latest/creative/preview-creative-request.json": "a84b78ab0930d2ccbc58752ced9288d733b87bcf18ecbb6631d345470318657b", - "https://adcontextprotocol.org/schemas/latest/creative/preview-creative-response.json": "ca9de195792f7beeddb5d7f6d148f8cf7693863706946aa44e3f6badd6c5e128", + "https://adcontextprotocol.org/schemas/latest/creative/preview-creative-request.json": "0df53d16ebebe536dae798fdf0f6f96b83570e4708612acf978b4fd31270e7ed", + "https://adcontextprotocol.org/schemas/latest/creative/preview-creative-response.json": "f3faae10eab5592c072f8a10cb84d2f8ddbc4ed9f21f13a059f6955d622402f0", "https://adcontextprotocol.org/schemas/latest/creative/preview-render.json": "b4837c5a4027d7034f90ddf914c2e5e1a90e4ba6ee76f68a1b622e8a6cf64fc3", "https://adcontextprotocol.org/schemas/latest/enums/action-source.json": "02379d8670666e56afe09639030a13f672329f6717e687677619a48d20531e30", "https://adcontextprotocol.org/schemas/latest/enums/adcp-domain.json": "ab0644259eb35ef366defd6e3e89f04087a7dde529d1dfd989a4ff5caa153b0e", "https://adcontextprotocol.org/schemas/latest/enums/age-verification-method.json": "49a68dfd8bc550272f6e359de0cfc66a269687b2f0b2df4cdc802b23d702e719", - "https://adcontextprotocol.org/schemas/latest/enums/asset-content-type.json": "ed3b4926634b591c84cb9c023c1c3e056df241f8bc641f011d0fc2ffafafc77b", + "https://adcontextprotocol.org/schemas/latest/enums/asset-content-type.json": "5dd1bcf8dda6be3876c2d0093e791c2869d795b555ad1228bb3eac89f172dca5", "https://adcontextprotocol.org/schemas/latest/enums/attribution-model.json": "2b7cf8e2100e1a46cecf03f244c2d3a009934546b6c42d9c6ed77eb8ca100fae", + "https://adcontextprotocol.org/schemas/latest/enums/audience-source.json": "1d2ab9b06ba4de73d97ab42b6d0371bea0d50b6083eb147f276e0733b5fd082d", "https://adcontextprotocol.org/schemas/latest/enums/auth-scheme.json": "492732201479760e022e7e20461874312403dab836af0f45c1382fd7b59c8b17", "https://adcontextprotocol.org/schemas/latest/enums/available-metric.json": "8b598a106359f0a64d9d629ac1b7c149d30456d55df3fe917c9200b177ed6fc6", "https://adcontextprotocol.org/schemas/latest/enums/catalog-action.json": "6926092f0b436ad3b4ca07803fb0105e4ed857c3d18e818f6e1879a0a11b18b6", @@ -154,6 +171,7 @@ "https://adcontextprotocol.org/schemas/latest/enums/catalog-type.json": "b3792d10786ca067b3ee6868e9fe45507200f498bdffc8d6a18ff03039e1dcbc", "https://adcontextprotocol.org/schemas/latest/enums/channels.json": "90cb43e6cca0dfb9f6424768a3a21d5200182eab0e998f50d2f3c91901fa3d22", "https://adcontextprotocol.org/schemas/latest/enums/co-branding-requirement.json": "7d533d01abfc5b134985e4a4611547a8d88601f2ad3dae166d30483433a5af8e", + "https://adcontextprotocol.org/schemas/latest/enums/consent-basis.json": "941c1bb97a73f283e8cad03675d956e2a809f8b866a6c817a2fd5449e4619e95", "https://adcontextprotocol.org/schemas/latest/enums/content-id-type.json": "1905f7e53b8297d88e052373329716811210190d70148d480a551404f259ea26", "https://adcontextprotocol.org/schemas/latest/enums/creative-action.json": "c8f56831b63d868ecdadb81787fbd9bda85f791387cc04d10a4f3f4774edf591", "https://adcontextprotocol.org/schemas/latest/enums/creative-agent-capability.json": "c107eededf219f2f63e9325ba1c204c08f4bfe05b72ab90d4848308f771a9454", @@ -166,14 +184,18 @@ "https://adcontextprotocol.org/schemas/latest/enums/delivery-type.json": "3934bfba9af3ef9469f73b85b15c61c058b79771e74ae7bc387afc95905feacc", "https://adcontextprotocol.org/schemas/latest/enums/demographic-system.json": "690540c7a5d554e0b2ba60d3e07244e6f7d78e6c364e61ce7c0a951631472106", "https://adcontextprotocol.org/schemas/latest/enums/device-platform.json": "4b9895324457519d04b076899c4e9f8e30f061a27f801685e23e3898b77e75a2", + "https://adcontextprotocol.org/schemas/latest/enums/device-type.json": "7f08b8fcbed96115d7eaababbffe9735a2f66981072976ff8946f4cfb59ac719", + "https://adcontextprotocol.org/schemas/latest/enums/digital-source-type.json": "b06b2218557cc1621dfff1d372a7aee9e1eaa20e0a16b161a0e648a1f9170111", "https://adcontextprotocol.org/schemas/latest/enums/dimension-unit.json": "405de49a9144c527466677ef3d3e5010b3cecea289f74b837958496da78045fb", + "https://adcontextprotocol.org/schemas/latest/enums/disclosure-position.json": "8131875b0f01ef31c2dc7e4147632ccd73a22c706f181f10eec09f8b2cdab2e4", "https://adcontextprotocol.org/schemas/latest/enums/distance-unit.json": "a625eebbd293ff2301bed5c7443e4d46f60cd64761d1b012530bb125c2597171", + "https://adcontextprotocol.org/schemas/latest/enums/error-code.json": "a08a35f8a60637f7d1a2c253f18bc8186e3bdba35a1c75720b0bde7fee104e57", "https://adcontextprotocol.org/schemas/latest/enums/event-type.json": "a3a488d7985e1b67ccf086569d0b3a60d975d50e544862b735c3805ba10d7b91", "https://adcontextprotocol.org/schemas/latest/enums/feed-format.json": "16e580fdb03ca645b37955427ad0cf0eed74145a7e4c627b8286b9bc22d59458", "https://adcontextprotocol.org/schemas/latest/enums/feedback-source.json": "56283eec28ab10c8a11f09959c0f3dbef2b5bf02b7df84599343cb9fb0e69f78", "https://adcontextprotocol.org/schemas/latest/enums/forecast-method.json": "692f2c79351023690c444532089f5ee9d60afb433eec60281cb0248c066f1212", "https://adcontextprotocol.org/schemas/latest/enums/forecast-range-unit.json": "c7dd6d5908f42d1e910f2d38aae9de1189f65158d62eba044e811ce430620a44", - "https://adcontextprotocol.org/schemas/latest/enums/forecastable-metric.json": "cda32eedea286cd884d1dfea6674077b6293888a3d34a91c70875d3f2bd999f2", + "https://adcontextprotocol.org/schemas/latest/enums/forecastable-metric.json": "9de989029a43471c515623f309e31a85cafd0eebaf0e3fb488f7afe07adc0d31", "https://adcontextprotocol.org/schemas/latest/enums/format-category.json": "efeeb37272b115eae42b3436600c00d75c29e94763148206993f2942a2a8ed99", "https://adcontextprotocol.org/schemas/latest/enums/format-id-parameter.json": "0783749a5ac1e2eff4cc523eec16e3a723e107b3718bd55e1bc671cf230b0a1f", "https://adcontextprotocol.org/schemas/latest/enums/frequency-cap-scope.json": "3275a767cbde71235e7a23d0d775b5d58afb1859936393b04eb2c7c976ef2493", @@ -184,12 +206,12 @@ "https://adcontextprotocol.org/schemas/latest/enums/javascript-module-type.json": "da4746acdce608ce9497f59e7fdbc97a649367e55aebe4b9675feb3176e8525e", "https://adcontextprotocol.org/schemas/latest/enums/landing-page-requirement.json": "5a09b76855c541b23ccf1aaa8ea87e77203d358b5c01c1b255722f21ac6327a4", "https://adcontextprotocol.org/schemas/latest/enums/markdown-flavor.json": "df8836754968fb1c02fc7725aa1bce56e315c4cec11515a19058b5d6a0cf782b", - "https://adcontextprotocol.org/schemas/latest/enums/media-buy-status.json": "70bd2fca8832137c3b504c4b80934fdf8a57bcb45c4e1bf821fc49096aae53f9", + "https://adcontextprotocol.org/schemas/latest/enums/media-buy-status.json": "ea2422ef950e6caca7798429f184ac4c272f8d0f79057231590a1fe359d541fb", "https://adcontextprotocol.org/schemas/latest/enums/metric-type.json": "41eafcfaf6206670aabecd65abd5c1f2d0c4af019a3e4055d26aecfdccfd0a75", "https://adcontextprotocol.org/schemas/latest/enums/metro-system.json": "f8cde6b0bd45c4fbabef8ff90e01c57ffc8d5a821d7f50b94de48a6ed7cf1641", "https://adcontextprotocol.org/schemas/latest/enums/notification-type.json": "be6d4177c9350e8fdced52773b483acf464beecd567934cf020e53c43e61afac", "https://adcontextprotocol.org/schemas/latest/enums/pacing.json": "6fb8e62bfdc40bb48e0620ed71310f21a821fdfbe644c625d01b6a68ba4e40bb", - "https://adcontextprotocol.org/schemas/latest/enums/postal-system.json": "20651c3197e7554ced864c3addb71a27ac257952988e5248db8f6f882fad12d6", + "https://adcontextprotocol.org/schemas/latest/enums/postal-system.json": "e1644c5d960fed57571b088cb72d25777b34ab335531b0d97e679c7dca33871a", "https://adcontextprotocol.org/schemas/latest/enums/preview-output-format.json": "b14841f4e6689b3a3d2afa5740b4787ba49961d39ede2f9b92b709ad7970b32e", "https://adcontextprotocol.org/schemas/latest/enums/pricing-model.json": "4378f72c1baec64766ff25c0352a3580df627fafe6d4f83ff2f6472cc8223c34", "https://adcontextprotocol.org/schemas/latest/enums/property-type.json": "7e047eb116c41b4682818f89c8b19573636a546500289d48be189945318ded83", @@ -200,10 +222,11 @@ "https://adcontextprotocol.org/schemas/latest/enums/signal-source.json": "a31754104d7fac059a8a4d62fe4515dd30825703a6f795ca1a740d24d63f88b8", "https://adcontextprotocol.org/schemas/latest/enums/signal-value-type.json": "fc2388a09442534f9931716d5ee4a4919e6b5242a2624c352b62c12a0443e32b", "https://adcontextprotocol.org/schemas/latest/enums/sort-direction.json": "85a2cf23403297069995f14498a495c566a5a8bce4409d468d9071e18338ce6e", + "https://adcontextprotocol.org/schemas/latest/enums/sort-metric.json": "73030f94b0fe202ac1ee26c4cdc30c287566721778d0d652cca8b7a272b94523", "https://adcontextprotocol.org/schemas/latest/enums/task-status.json": "b45adb6b28b0b35ea5cf5564ec22d7b692e66c40d10d891d6dfe6661035c82b0", - "https://adcontextprotocol.org/schemas/latest/enums/task-type.json": "deea623ca8d42186fc6ca09a2a1e4e0493f3c6b9679d36c41a4d291bf9470a3d", + "https://adcontextprotocol.org/schemas/latest/enums/task-type.json": "3ff3ce4cbd94c5a06624d3fae8dce7288847222ed2bf8f810c0a42a24a4c6689", "https://adcontextprotocol.org/schemas/latest/enums/transport-mode.json": "de48169f9bc627ee6a41d9f20803f7addd3589659e166ee9a327d58ae26b8a6c", - "https://adcontextprotocol.org/schemas/latest/enums/uid-type.json": "9f5a6855a5fa45aa6053e3f1aa43ca87507e966e4fadf9a37e3057070162bb81", + "https://adcontextprotocol.org/schemas/latest/enums/uid-type.json": "5d3ce6721a2b3d650f6e007c6dc886df498aba3e6954b734de6f4419f2011477", "https://adcontextprotocol.org/schemas/latest/enums/universal-macro.json": "de56be91b9aa6f2a096c0c8371abe8c7897059d4d2a436a3ff9e51f19796d386", "https://adcontextprotocol.org/schemas/latest/enums/update-frequency.json": "657d0460d5675ddcd7edb4cf92655b56a93cd724ab3c0b4094241ef91ca644c8", "https://adcontextprotocol.org/schemas/latest/enums/url-asset-type.json": "8cb3c557a43a0d5f85cd9c7b83366910c570349c6ab8625583ca66454fc88c03", @@ -214,51 +237,51 @@ "https://adcontextprotocol.org/schemas/latest/enums/webhook-response-type.json": "72ba18db0a6fabd814195e895841e4cd6af5b54c4d6a954552c36284da5c4075", "https://adcontextprotocol.org/schemas/latest/enums/webhook-security-method.json": "80c1a7622b750af3bcfd2695569a3b348e42d5c8614378c27a65f8c7374e9165", "https://adcontextprotocol.org/schemas/latest/extensions/extension-meta.json": "96653ac32a37fcab33ba562c62f09af7362c81cca7f95fa181499dd9d3b8eabe", - "https://adcontextprotocol.org/schemas/latest/extensions/index.json": "ed0e69bc6c66456553d31381636a415a2d448e19f233f91fa97567757226edd0", - "https://adcontextprotocol.org/schemas/latest/media-buy/build-creative-request.json": "1b4e3f8560e45abb6129f12da0908171c13962eb48b81d0d6d3ec84b07703960", + "https://adcontextprotocol.org/schemas/latest/extensions/index.json": "92a7c79c137f7a16ab7fced28d2ffefedbb8b31a2153bbbad82ea6892acc30a3", + "https://adcontextprotocol.org/schemas/latest/media-buy/build-creative-request.json": "b1683d732ba7c070502d62440a45283b17a42a35b059e90476f2c446402b8208", "https://adcontextprotocol.org/schemas/latest/media-buy/build-creative-response.json": "b59144e5ed7a39ad380a070a2f5e5ce3073780debba4cf77eb59e729ffbfbcfc", "https://adcontextprotocol.org/schemas/latest/media-buy/create-media-buy-async-response-input-required.json": "397bb7a973f943bdb2c53b36822c93ecdc815d3e5184a1129cad8648d88f378a", "https://adcontextprotocol.org/schemas/latest/media-buy/create-media-buy-async-response-submitted.json": "7c9502344a561c3aca8752e6d95918444eb905b82413af0c6a8e4782963ce4f2", "https://adcontextprotocol.org/schemas/latest/media-buy/create-media-buy-async-response-working.json": "f8023b1dd59955843ecf2ee3c79a81ca83411ced35daa58a6e7210ad88678e00", - "https://adcontextprotocol.org/schemas/latest/media-buy/create-media-buy-request.json": "2c4d7d209d92fc4d91c481061e7535a35f2858f28fd9809b22cb562279ff4702", + "https://adcontextprotocol.org/schemas/latest/media-buy/create-media-buy-request.json": "2d5244697de961318d1ce60a62471514efb6953e6bcbad59cfe1d46d4ca62afd", "https://adcontextprotocol.org/schemas/latest/media-buy/create-media-buy-response.json": "6eeb5eec60d9a8f8dbd1b9d11b1f947de7e4148817b83ec2a6626457626939b9", - "https://adcontextprotocol.org/schemas/latest/media-buy/get-media-buy-delivery-request.json": "8ddee92dcb78c2b250ebc9e89bbf21bf9128a6e3884d0fee2fbf38256e33ca12", - "https://adcontextprotocol.org/schemas/latest/media-buy/get-media-buy-delivery-response.json": "7dea150b6992b216c211c0eb80328acb7ec77285aa2cad7bd7269928d907978a", - "https://adcontextprotocol.org/schemas/latest/media-buy/get-media-buys-request.json": "1b4a8d743166c14b94f28f2c8d5155fc87f844eff7cd940cc77e05795551d7d6", - "https://adcontextprotocol.org/schemas/latest/media-buy/get-media-buys-response.json": "b73b534a88c19db9aeea91d28486aee6f544593706ab8ae34be5dcd003770568", + "https://adcontextprotocol.org/schemas/latest/media-buy/get-media-buy-delivery-request.json": "b760b2f75e1d3ea8613d10841d238e301546d8cf5498bf5040d5a18401248689", + "https://adcontextprotocol.org/schemas/latest/media-buy/get-media-buy-delivery-response.json": "e939f171e70b637b5233cae215c8c67c93d75473bc654fb45991d15cfc302d76", + "https://adcontextprotocol.org/schemas/latest/media-buy/get-media-buys-request.json": "13b796eef705559e7f188b519161c00f966509f2141633701f75af74602db3cf", + "https://adcontextprotocol.org/schemas/latest/media-buy/get-media-buys-response.json": "26f5be2b2ae372d43aebf5d034e620b19a5678382a03b58e6590d3e399f2b4d2", "https://adcontextprotocol.org/schemas/latest/media-buy/get-products-async-response-input-required.json": "107e27baf08f80a97a0de83a24faaf37cae5c0c3112ba707ff191af1c2ad4ea6", "https://adcontextprotocol.org/schemas/latest/media-buy/get-products-async-response-submitted.json": "f0cbf91c1e1ccc5e2c84739b8ebfbbf73cb4f64d0067847bc3eb19ec1992aa72", "https://adcontextprotocol.org/schemas/latest/media-buy/get-products-async-response-working.json": "62be745552eeaa0aa72fc8a06b0dc89d138264ed5150158f1061cd5043c99dfa", - "https://adcontextprotocol.org/schemas/latest/media-buy/get-products-request.json": "2d74f3aebef6162e979d5c928dd15b6809296f36a199c930b6918ef7c53d8bfa", - "https://adcontextprotocol.org/schemas/latest/media-buy/get-products-response.json": "e8f124faf28feca72c40d3e72ff9473aab47cf4aa34bc8ba931c06fcb1de99c7", - "https://adcontextprotocol.org/schemas/latest/media-buy/list-creative-formats-request.json": "2a90a84dbca91edde787fcbe0416774096d4ea78a7a162dfae01864b83f31c1d", + "https://adcontextprotocol.org/schemas/latest/media-buy/get-products-request.json": "63b134d0cf1fd92f909ed1a0a73eb0dfd9ec702e2e26b0e7e7f4cb33241b7b83", + "https://adcontextprotocol.org/schemas/latest/media-buy/get-products-response.json": "7c4ce4abcb82e76009f9125599b4d834deaff1bfb62d65d98bd86b38fb692080", + "https://adcontextprotocol.org/schemas/latest/media-buy/list-creative-formats-request.json": "16a087559e0e43732cd0551c7ae16dc077a8aa06d487207d20c496a4cbabdac9", "https://adcontextprotocol.org/schemas/latest/media-buy/list-creative-formats-response.json": "62fe8cb5f240de313dd7d2ca2dad365407abf7899c7c5a4babf9c329e0d69cb9", "https://adcontextprotocol.org/schemas/latest/media-buy/list-creatives-request.json": "2fd4a07bd836cf44cf40bdddafb2f1b07881e616f6eca9c2d45381071eedf9b8", - "https://adcontextprotocol.org/schemas/latest/media-buy/list-creatives-response.json": "c2b697ae6756b3418aaf01f2e4270a25e70f6ee0e3e57f8ada72a9e1a7da3cd7", + "https://adcontextprotocol.org/schemas/latest/media-buy/list-creatives-response.json": "0fed4073d91b74fbe98491abe52168dc2ded39adb102a328e3017b1ac9387bd0", "https://adcontextprotocol.org/schemas/latest/media-buy/log-event-request.json": "79f382bc795e81123e01aa570e8c7fb31547f77789edb64edd5f6acc4a2c2756", "https://adcontextprotocol.org/schemas/latest/media-buy/log-event-response.json": "d561a4d0937e98a2e5a871df3ff36fa4b0201960a663661dd1d830d29f154a7c", - "https://adcontextprotocol.org/schemas/latest/media-buy/package-request.json": "dd28f52862a92d6fd51152ffd22272c01706b9fa4090f52db4b19f0848d7cca9", - "https://adcontextprotocol.org/schemas/latest/media-buy/package-update.json": "690290dfebb061d2290d18eea667e475f446750345171880627b975d741f6ab8", - "https://adcontextprotocol.org/schemas/latest/media-buy/provide-performance-feedback-request.json": "8fff8a6e58cf2f8dd95e2e1cb53ef1b3c2154991a577547a2f8c75028867a81b", + "https://adcontextprotocol.org/schemas/latest/media-buy/package-request.json": "cc6ff3469a22a6eadb67b996cf2e503eed90a1e47e3d8a6f7a496b39c01150a3", + "https://adcontextprotocol.org/schemas/latest/media-buy/package-update.json": "ba965686ecedebd66cec330e135b8daa072b4b8c26c98d0691b893b7e87c0ad6", + "https://adcontextprotocol.org/schemas/latest/media-buy/provide-performance-feedback-request.json": "30b9cbb71776718fe02069596cfc1c57b070c69e8efbe1393af173bc5672fba2", "https://adcontextprotocol.org/schemas/latest/media-buy/provide-performance-feedback-response.json": "679838758bb15e911b6574a28f5a9048de1e8baa067e71052d9d7d430201636b", - "https://adcontextprotocol.org/schemas/latest/media-buy/sync-audiences-request.json": "d3960550cd7c8bf90a27912d1e1b7a365915ede412c2045e3542305c346c56ea", - "https://adcontextprotocol.org/schemas/latest/media-buy/sync-audiences-response.json": "37958940d040832ad05713f96a269debb8651a1c62389bbeaacad9d70c181d3f", + "https://adcontextprotocol.org/schemas/latest/media-buy/sync-audiences-request.json": "0ed83f08d46758865c30a63b721dddb874af5955baba0094248d2bb341ac5c4f", + "https://adcontextprotocol.org/schemas/latest/media-buy/sync-audiences-response.json": "8bda101ab6048f5c0d46feb59f4138067edf8980f815bc039657f504a486f1e2", "https://adcontextprotocol.org/schemas/latest/media-buy/sync-catalogs-async-response-input-required.json": "524ab6b3ab587af92a3358b8aea639060291750e121027e80c60b6956e77b559", "https://adcontextprotocol.org/schemas/latest/media-buy/sync-catalogs-async-response-submitted.json": "104da8ec5fcec069e94cbe42e95db04a1a842d7d8ca0fe35424cbf11f57d5e91", "https://adcontextprotocol.org/schemas/latest/media-buy/sync-catalogs-async-response-working.json": "c8147b47bdc9c5314a5a130610ae83bcb244f7bf18403dcfe1192c65e712f26b", - "https://adcontextprotocol.org/schemas/latest/media-buy/sync-catalogs-request.json": "b87260831eb5bd5273cd771d5227f40147395a755d3880db7c70e235678739b2", + "https://adcontextprotocol.org/schemas/latest/media-buy/sync-catalogs-request.json": "ee71808d456122af70604f4d771db5a0ee37ae0228288d7f0b6da4a1a4f3e259", "https://adcontextprotocol.org/schemas/latest/media-buy/sync-catalogs-response.json": "f42a8d9ca96c58011bd17ac46481fe90c9517794726421f82cf2c40d1c35deb3", "https://adcontextprotocol.org/schemas/latest/media-buy/sync-creatives-async-response-input-required.json": "62b4e86135542f4076b7bcf6edf157a7bb919e168ea97d0afb1ef1fe4ff2b8dd", "https://adcontextprotocol.org/schemas/latest/media-buy/sync-creatives-async-response-submitted.json": "0a6df44b4224a4b38d25673d0f813022e124c7ac0bcc7eccb95e673849af7368", "https://adcontextprotocol.org/schemas/latest/media-buy/sync-creatives-async-response-working.json": "5a336a8217d13fa112ac3dd1ed2d2c707f8abd9bb9ff05af674c22138dc24c90", - "https://adcontextprotocol.org/schemas/latest/media-buy/sync-creatives-request.json": "f319c5448b25ae765e39b144689bfdbfd7fbc04f0b8ea4a7ef256101924a8089", + "https://adcontextprotocol.org/schemas/latest/media-buy/sync-creatives-request.json": "18eda1079f8acd569930e0c241d1d7f3ff44df68dcb8b7d17a0a0f1b76e0375e", "https://adcontextprotocol.org/schemas/latest/media-buy/sync-creatives-response.json": "a6f40b4a549401a9e440f03130f9c9fbc9645dd1f6f613e7939dd6cf1925d6bd", - "https://adcontextprotocol.org/schemas/latest/media-buy/sync-event-sources-request.json": "9a89c03911906e3e6f05510cd1f64a548778bb9fec72fb220ca1c41580d748ff", + "https://adcontextprotocol.org/schemas/latest/media-buy/sync-event-sources-request.json": "44bf656a9381e8350283ff1896277a80d250dd28f6986aadbdaabd10003294d5", "https://adcontextprotocol.org/schemas/latest/media-buy/sync-event-sources-response.json": "fc4303557f6cee87078ffb294444d8b9d2ab4b975e3d7fbf53a04b5bc0bcd401", "https://adcontextprotocol.org/schemas/latest/media-buy/update-media-buy-async-response-input-required.json": "73ecee00cf0babc200778621b78148a54327df3436dedf349d83c734a99fc4e4", "https://adcontextprotocol.org/schemas/latest/media-buy/update-media-buy-async-response-submitted.json": "d4736323140cf6db5522e1de5aeb1023f8a7b608d319ede26b86d28c57bea0ef", "https://adcontextprotocol.org/schemas/latest/media-buy/update-media-buy-async-response-working.json": "ccca3256ac3179a2125922718efde61367b9046bdfdee156bdaa72c1398f2ed3", - "https://adcontextprotocol.org/schemas/latest/media-buy/update-media-buy-request.json": "818adb8936b04ba9917e80c7e6c23c2e8e00ce417dcdf57636247bce7feb8316", + "https://adcontextprotocol.org/schemas/latest/media-buy/update-media-buy-request.json": "b61ccfa4c501f4fa99825a3fcb9e33093e51193bd65a09636ac04ea84dc2ff95", "https://adcontextprotocol.org/schemas/latest/media-buy/update-media-buy-response.json": "94a5dc2b031c552a74c32cdbc9b77920cb0d3f61242a90292f69582b86e72402", "https://adcontextprotocol.org/schemas/latest/pricing-options/cpa-option.json": "2edbea9af419fe4db7d959ac65ef067c0c7dadcb0f32bdcb12ebafa4a041f73f", "https://adcontextprotocol.org/schemas/latest/pricing-options/cpc-option.json": "30933b4470569e5cb8316071fd4549ce0ab0266af1a29ac06841e003a828b981", @@ -284,16 +307,16 @@ "https://adcontextprotocol.org/schemas/latest/property/property-feature-definition.json": "2030cb01aca722d5051b45399e4297f9e3307e71bb5e9743cd6fdaabcc0726d1", "https://adcontextprotocol.org/schemas/latest/property/property-feature.json": "f6bb5d571b0c9cca78f4f97e867651a8798103ba3eb460cc87c7f9144929541a", "https://adcontextprotocol.org/schemas/latest/property/property-list-changed-webhook.json": "4bb10e21aee5c58dc64fa0bba9e79798d3aa958e1eb02a6f028ea1799f5c03bb", - "https://adcontextprotocol.org/schemas/latest/property/property-list-filters.json": "7e9f071111eb20ebbb60a0c68dbb1cb3543bf29ad5608fdab6642532f05a1537", + "https://adcontextprotocol.org/schemas/latest/property/property-list-filters.json": "036b9b7902b20512549ab38f6fc57fd952d676cb3d8e61c60e77d0556b71c4c2", "https://adcontextprotocol.org/schemas/latest/property/property-list.json": "d6830f2541a929e72587b11a5e63c437fa806fadaa9033d93b14491ea9198526", "https://adcontextprotocol.org/schemas/latest/property/update-property-list-request.json": "6ca7e3ddfebc4ce89ab2a590d3858b97e1d6da4a57bbdddf498fd30d85d6ec56", "https://adcontextprotocol.org/schemas/latest/property/update-property-list-response.json": "3d5e6ceca2d6273c77bab370725e41b95077deb2f55f37ca62ce440c8353b1ca", "https://adcontextprotocol.org/schemas/latest/protocol/get-adcp-capabilities-request.json": "a93fa59dac256984ea636dbfb01f99d2387ff479bf379b968372787bb0f832c7", - "https://adcontextprotocol.org/schemas/latest/protocol/get-adcp-capabilities-response.json": "98898f4f68b7167dfa4a950e0564cd3eb7508292f886493e0cbda57c054f5e0e", - "https://adcontextprotocol.org/schemas/latest/signals/activate-signal-request.json": "5791ed345768830e7e0eaf72d03027384550ccbd79b9acc5ef45c5f34bf705f2", + "https://adcontextprotocol.org/schemas/latest/protocol/get-adcp-capabilities-response.json": "8f0dd510e31f8b692643e4985c34843af5ee6e4fdf9e3b3f5821a675d81b9c84", + "https://adcontextprotocol.org/schemas/latest/signals/activate-signal-request.json": "81dca01b1b460fd9f7ec848998dd215303307197fbc977bdf1b833a05861f437", "https://adcontextprotocol.org/schemas/latest/signals/activate-signal-response.json": "e1cd9be827aba98cab9f0de0341bb449b3739ebb9c20caef9a0eb0dbcb087ca2", - "https://adcontextprotocol.org/schemas/latest/signals/get-signals-request.json": "9cc79ae0db10f5e4d02f19123e3312f3e57bd6eee4b534f72065ce933e6bfb9e", - "https://adcontextprotocol.org/schemas/latest/signals/get-signals-response.json": "8f1acccc47153d64aad213ec0093c1076a915ba6981a11c76f2a84dbbbe8ac9d", + "https://adcontextprotocol.org/schemas/latest/signals/get-signals-request.json": "d0c5cdadcd96e34867287d00153ef628e5a1f2035de315da49637ba495641f48", + "https://adcontextprotocol.org/schemas/latest/signals/get-signals-response.json": "1fc79a60a8ab98c5a60964ff1e608b1fb4af79dee743ad0602acec90195a6f78", "https://adcontextprotocol.org/schemas/latest/sponsored-intelligence/si-capabilities.json": "a583b04f6acd5c8418c6b9613842e12d6b00a1f36241990d02b17c71ebf74356", "https://adcontextprotocol.org/schemas/latest/sponsored-intelligence/si-get-offering-request.json": "175d7cbadd0f5fccabab09158986f51edd459ba4e777a6731cf850947f0493f7", "https://adcontextprotocol.org/schemas/latest/sponsored-intelligence/si-get-offering-response.json": "e0cc6da05594aa7080319bbc506cf56d3f7c541504cde86e27573917078410e8", diff --git a/schemas/cache/account/get-account-financials-request.json b/schemas/cache/account/get-account-financials-request.json new file mode 100644 index 00000000..dc7985b2 --- /dev/null +++ b/schemas/cache/account/get-account-financials-request.json @@ -0,0 +1,63 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "description": "Request financial status for an operator-billed account. Returns spend summary, credit/balance status, and invoice history. Only applicable when the seller declares account_financials capability.", + "examples": [ + { + "data": { + "account": { + "account_id": "acc_acme_001" + } + }, + "description": "Query by account ID for current billing cycle" + }, + { + "data": { + "account": { + "brand": { + "domain": "acme-corp.com" + }, + "operator": "acme-corp.com" + }, + "period": { + "end": "2026-01-31", + "start": "2026-01-01" + } + }, + "description": "Query by natural key for a specific period" + }, + { + "data": { + "account": { + "brand": { + "brand_id": "spark", + "domain": "nova-brands.com" + }, + "operator": "pinnacle-media.com" + } + }, + "description": "Agency querying financials for a client account" + } + ], + "properties": { + "account": { + "$ref": "../core/account-ref.json", + "description": "Account to query financials for. Must be an operator-billed account." + }, + "context": { + "$ref": "../core/context.json" + }, + "ext": { + "$ref": "../core/ext.json" + }, + "period": { + "$ref": "../core/date-range.json", + "description": "Date range for the spend summary. Defaults to the current billing cycle if omitted." + } + }, + "required": [ + "account" + ], + "title": "Get Account Financials Request", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/account/get-account-financials-response.json b/schemas/cache/account/get-account-financials-response.json new file mode 100644 index 00000000..b3290b7b --- /dev/null +++ b/schemas/cache/account/get-account-financials-response.json @@ -0,0 +1,319 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Financial status for an operator-billed account. Returns spend summary, credit/balance status, payment status, and invoice history. The level of detail varies by seller \u2014 only account, currency, and period are guaranteed on success.", + "examples": [ + { + "data": { + "account": { + "account_id": "acc_acme_001" + }, + "credit": { + "available_credit": 54770.0, + "credit_limit": 100000.0, + "utilization_percent": 45.23 + }, + "currency": "USD", + "invoices": [ + { + "amount": 38500.0, + "due_date": "2026-02-28", + "invoice_id": "inv_2026_01", + "paid_date": "2026-02-15", + "period": { + "end": "2026-01-31", + "start": "2026-01-01" + }, + "status": "paid" + } + ], + "payment_status": "current", + "payment_terms": "net_30", + "period": { + "end": "2026-02-28", + "start": "2026-02-01" + }, + "spend": { + "media_buy_count": 3, + "total_spend": 45230.0 + }, + "timezone": "America/New_York" + }, + "description": "Credit account \u2014 current, with invoice history" + }, + { + "data": { + "account": { + "brand": { + "domain": "acme-corp.com" + }, + "operator": "acme-corp.com" + }, + "balance": { + "available": 1800.0, + "last_top_up": { + "amount": 10000.0, + "date": "2026-02-01" + } + }, + "currency": "USD", + "payment_status": "current", + "payment_terms": "prepay", + "period": { + "end": "2026-02-28", + "start": "2026-02-01" + }, + "spend": { + "media_buy_count": 2, + "total_spend": 8200.0 + }, + "timezone": "America/Los_Angeles" + }, + "description": "Prepay account with low balance" + }, + { + "data": { + "errors": [ + { + "code": "UNSUPPORTED_FEATURE", + "message": "Financial data is not available for agent-billed accounts. The agent's own billing system is the source of truth." + } + ] + }, + "description": "Agent-billed account \u2014 not supported" + } + ], + "oneOf": [ + { + "additionalProperties": true, + "description": "Financial data retrieved successfully", + "not": { + "required": [ + "errors" + ] + }, + "properties": { + "account": { + "$ref": "../core/account-ref.json", + "description": "Account reference, echoed from the request" + }, + "balance": { + "description": "Prepay balance. Present for prepay accounts.", + "properties": { + "available": { + "description": "Remaining prepaid balance", + "minimum": 0, + "type": "number" + }, + "last_top_up": { + "description": "Most recent balance top-up", + "properties": { + "amount": { + "description": "Top-up amount", + "minimum": 0, + "type": "number" + }, + "date": { + "description": "Date of top-up", + "format": "date", + "type": "string" + } + }, + "required": [ + "amount", + "date" + ], + "type": "object" + } + }, + "required": [ + "available" + ], + "type": "object" + }, + "context": { + "$ref": "../core/context.json" + }, + "credit": { + "description": "Credit status. Present for credit-based accounts (payment_terms like net_30).", + "properties": { + "available_credit": { + "description": "Remaining credit available (credit_limit minus outstanding balance)", + "type": "number" + }, + "credit_limit": { + "description": "Maximum outstanding balance allowed", + "minimum": 0, + "type": "number" + }, + "utilization_percent": { + "description": "Credit utilization as a percentage (0-100)", + "maximum": 100, + "minimum": 0, + "type": "number" + } + }, + "required": [ + "credit_limit", + "available_credit" + ], + "type": "object" + }, + "currency": { + "description": "ISO 4217 currency code for all monetary amounts in this response", + "pattern": "^[A-Z]{3}$", + "type": "string" + }, + "ext": { + "$ref": "../core/ext.json" + }, + "invoices": { + "description": "Recent invoices. Sellers may limit the number returned.", + "items": { + "properties": { + "amount": { + "description": "Invoice total in currency", + "minimum": 0, + "type": "number" + }, + "due_date": { + "description": "Payment due date", + "format": "date", + "type": "string" + }, + "invoice_id": { + "description": "Seller-assigned invoice identifier", + "type": "string" + }, + "paid_date": { + "description": "Date payment was received. Present when status is 'paid'.", + "format": "date", + "type": "string" + }, + "period": { + "$ref": "../core/date-range.json", + "description": "Billing period covered by this invoice" + }, + "status": { + "description": "Invoice status", + "enum": [ + "draft", + "issued", + "paid", + "past_due", + "void" + ], + "type": "string" + } + }, + "required": [ + "invoice_id", + "amount", + "status" + ], + "type": "object" + }, + "type": "array" + }, + "payment_status": { + "description": "Overall payment status. current: all obligations met. past_due: one or more invoices overdue. suspended: account suspended due to payment issues.", + "enum": [ + "current", + "past_due", + "suspended" + ], + "type": "string" + }, + "payment_terms": { + "description": "Payment terms in effect (e.g., 'net_30', 'prepay')", + "type": "string" + }, + "period": { + "$ref": "../core/date-range.json", + "description": "The actual period covered by spend data. May differ from the requested period if the seller adjusts to billing cycle boundaries." + }, + "spend": { + "description": "Spend summary for the period", + "properties": { + "media_buy_count": { + "description": "Number of active media buys in the period", + "minimum": 0, + "type": "integer" + }, + "total_spend": { + "description": "Total spend in the period, in currency", + "minimum": 0, + "type": "number" + } + }, + "required": [ + "total_spend" + ], + "type": "object" + }, + "timezone": { + "description": "IANA timezone of the seller's billing day boundaries (e.g., 'America/New_York'). All dates in this response \u2014 period, invoice periods, due dates \u2014 are calendar dates in this timezone. Buyers in a different timezone should expect spend boundaries to differ from their own calendar day.", + "type": "string" + } + }, + "required": [ + "account", + "currency", + "period", + "timezone" + ], + "title": "GetAccountFinancialsSuccess", + "type": "object" + }, + { + "additionalProperties": true, + "description": "Operation failed \u2014 financials not available", + "not": { + "anyOf": [ + { + "required": [ + "account" + ] + }, + { + "required": [ + "currency" + ] + }, + { + "required": [ + "period" + ] + }, + { + "required": [ + "timezone" + ] + } + ] + }, + "properties": { + "context": { + "$ref": "../core/context.json" + }, + "errors": { + "description": "Operation-level errors", + "items": { + "$ref": "../core/error.json" + }, + "minItems": 1, + "type": "array" + }, + "ext": { + "$ref": "../core/ext.json" + } + }, + "required": [ + "errors" + ], + "title": "GetAccountFinancialsError", + "type": "object" + } + ], + "title": "Get Account Financials Response", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/account/list-accounts-request.json b/schemas/cache/account/list-accounts-request.json index 67e0e625..5f00117c 100644 --- a/schemas/cache/account/list-accounts-request.json +++ b/schemas/cache/account/list-accounts-request.json @@ -21,6 +21,7 @@ "enum": [ "active", "pending_approval", + "rejected", "payment_required", "suspended", "closed" diff --git a/schemas/cache/account/list-accounts-response.json b/schemas/cache/account/list-accounts-response.json index 6079e163..2afe44ec 100644 --- a/schemas/cache/account/list-accounts-response.json +++ b/schemas/cache/account/list-accounts-response.json @@ -8,22 +8,41 @@ "accounts": [ { "account_id": "acc_acme_pinnacle", + "account_scope": "operator_brand", "advertiser": "Acme Corp", + "billing": "operator", "billing_proxy": "Pinnacle Media", + "brand": { + "domain": "acme-corp.com" + }, "name": "Acme c/o Pinnacle", + "operator": "pinnacle-media.com", "status": "active" }, { "account_id": "acc_nova_pinnacle", + "account_scope": "operator_brand", "advertiser": "Nova Brands", + "billing": "operator", "billing_proxy": "Pinnacle Media", + "brand": { + "brand_id": "spark", + "domain": "nova-brands.com" + }, "name": "Nova c/o Pinnacle", + "operator": "pinnacle-media.com", "status": "active" }, { "account_id": "acc_pinnacle", + "account_scope": "operator", "advertiser": "Pinnacle Media", + "billing": "operator", + "brand": { + "domain": "pinnacle-media.com" + }, "name": "Pinnacle", + "operator": "pinnacle-media.com", "status": "active" } ], @@ -39,8 +58,14 @@ "accounts": [ { "account_id": "acc_acme_direct", + "account_scope": "brand", "advertiser": "Acme Corp", + "billing": "operator", + "brand": { + "domain": "acme-corp.com" + }, "name": "Acme", + "operator": "acme-corp.com", "rate_card": "acme_vip_2024", "status": "active" } diff --git a/schemas/cache/account/report-usage-request.json b/schemas/cache/account/report-usage-request.json new file mode 100644 index 00000000..a6c37d1b --- /dev/null +++ b/schemas/cache/account/report-usage-request.json @@ -0,0 +1,142 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "description": "Reports how a vendor's service was consumed after campaign delivery. Used by orchestrators (DSPs, storefronts) to inform vendor agents (signals, governance, creative) what was used so the vendor can track earned revenue and verify billing. Records can span multiple accounts and campaigns in a single request.", + "examples": [ + { + "data": { + "idempotency_key": "550e8400-e29b-41d4-a716-446655440000", + "reporting_period": { + "end": "2025-03-31T23:59:59Z", + "start": "2025-03-01T00:00:00Z" + }, + "usage": [ + { + "account": { + "account_id": "acct_pinnacle_signals" + }, + "buyer_campaign_ref": "mb_spring_launch_001", + "currency": "USD", + "impressions": 4200000, + "media_spend": 21000.0, + "pricing_option_id": "po_lux_auto_cpm", + "signal_agent_segment_id": "luxury_auto_intenders", + "vendor_cost": 2100.0 + } + ] + }, + "description": "Signal usage for a single campaign" + }, + { + "data": { + "reporting_period": { + "end": "2025-03-31T23:59:59Z", + "start": "2025-03-01T00:00:00Z" + }, + "usage": [ + { + "account": { + "account_id": "acct_pinnacle_signals" + }, + "buyer_campaign_ref": "mb_spring_q1_001", + "currency": "USD", + "impressions": 2100000, + "pricing_option_id": "po_lux_auto_cpm", + "signal_agent_segment_id": "luxury_auto_intenders", + "vendor_cost": 1050.0 + }, + { + "account": { + "account_id": "acct_nova" + }, + "buyer_campaign_ref": "mb_summer_preview_002", + "currency": "USD", + "impressions": 800000, + "pricing_option_id": "po_eco_cpm", + "signal_agent_segment_id": "eco_conscious_shoppers", + "vendor_cost": 400.0 + } + ] + }, + "description": "Multi-account batch across two campaigns" + } + ], + "properties": { + "context": { + "$ref": "../core/context.json" + }, + "ext": { + "$ref": "../core/ext.json" + }, + "idempotency_key": { + "description": "Client-generated unique key for this request. If a request with the same key has already been accepted, the server returns the original response without re-processing. Use a UUID or other unique identifier. Prevents duplicate billing on retries.", + "type": "string" + }, + "reporting_period": { + "$ref": "../core/datetime-range.json", + "description": "The time range covered by this usage report. Applies to all records in the request." + }, + "usage": { + "description": "One or more usage records. Each record is self-contained: it carries its own account and buyer_campaign_ref, allowing a single request to span multiple accounts and campaigns.", + "items": { + "additionalProperties": true, + "properties": { + "account": { + "$ref": "../core/account-ref.json", + "description": "Account for this usage record." + }, + "buyer_campaign_ref": { + "description": "The buyer's campaign reference (e.g., a media_buy_id). Used to group records by campaign.", + "type": "string" + }, + "currency": { + "description": "ISO 4217 currency code.", + "pattern": "^[A-Z]{3}$", + "type": "string" + }, + "impressions": { + "description": "Impressions delivered using this vendor service.", + "minimum": 0, + "type": "integer" + }, + "media_spend": { + "description": "Media spend in currency for the period. Required when a percent_of_media pricing model was used, so the vendor can verify the applied rate.", + "minimum": 0, + "type": "number" + }, + "pricing_option_id": { + "description": "Pricing option identifier from the vendor's discovery response (e.g., get_signals, list_content_standards). The vendor uses this to verify the correct rate was applied.", + "type": "string" + }, + "signal_agent_segment_id": { + "description": "Signal identifier from get_signals. Required for signals agents.", + "type": "string" + }, + "standards_id": { + "description": "Content standards configuration identifier. Required for governance agents.", + "type": "string" + }, + "vendor_cost": { + "description": "Amount owed to the vendor for this record, denominated in currency.", + "minimum": 0, + "type": "number" + } + }, + "required": [ + "account", + "vendor_cost", + "currency" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "reporting_period", + "usage" + ], + "title": "Report Usage Request", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/account/report-usage-response.json b/schemas/cache/account/report-usage-response.json new file mode 100644 index 00000000..8a0fc81c --- /dev/null +++ b/schemas/cache/account/report-usage-response.json @@ -0,0 +1,55 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "description": "Response from report_usage. Partial acceptance is valid \u2014 records that pass validation are stored even when others fail.", + "examples": [ + { + "data": { + "accepted": 2 + }, + "description": "All records accepted" + }, + { + "data": { + "accepted": 1, + "errors": [ + { + "code": "INVALID_PRICING_OPTION", + "field": "usage[1].pricing_option_id", + "message": "pricing_option_id 'po_unknown' does not exist on this account" + } + ] + }, + "description": "Partial acceptance \u2014 one record failed validation" + } + ], + "properties": { + "accepted": { + "description": "Number of usage records successfully stored.", + "minimum": 0, + "type": "integer" + }, + "context": { + "$ref": "../core/context.json" + }, + "errors": { + "description": "Validation errors for individual records. The field property identifies which record failed (e.g., 'usage[1].pricing_option_id').", + "items": { + "$ref": "../core/error.json" + }, + "type": "array" + }, + "ext": { + "$ref": "../core/ext.json" + }, + "sandbox": { + "description": "When true, the account is a sandbox account and no billing occurred.", + "type": "boolean" + } + }, + "required": [ + "accepted" + ], + "title": "Report Usage Response", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/account/sync-accounts-request.json b/schemas/cache/account/sync-accounts-request.json index 94ded89f..1e79bd41 100644 --- a/schemas/cache/account/sync-accounts-request.json +++ b/schemas/cache/account/sync-accounts-request.json @@ -1,22 +1,26 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, - "description": "Sync advertiser accounts with a seller using upsert semantics. The agent declares which brands it represents, who operates each seat, and the billing model. The seller provisions or links accounts accordingly, returning per-account status. Brands are identified by house domain + brand_id, resolved via /.well-known/brand.json.", + "description": "Sync advertiser accounts with a seller using upsert semantics. The agent declares which brands it represents, who operates on each brand's behalf, and the billing model. The seller provisions or links accounts accordingly, returning per-account status.", "examples": [ { "data": { "accounts": [ { - "billing": "brand", - "brand_id": "dove", - "house": "unilever.com", - "operator": "mindshare.com" + "billing": "operator", + "brand": { + "brand_id": "spark", + "domain": "nova-brands.com" + }, + "operator": "pinnacle-media.com" }, { "billing": "agent", - "brand_id": "tide", - "house": "pg.com", - "operator": "groupm.com" + "brand": { + "brand_id": "glow", + "domain": "nova-brands.com" + }, + "operator": "pinnacle-media.com" } ] }, @@ -26,25 +30,34 @@ "data": { "accounts": [ { - "billing": "brand", - "house": "acme-corp.com" + "billing": "operator", + "brand": { + "domain": "acme-corp.com" + }, + "operator": "acme-corp.com" } ] }, - "description": "Brand buying direct, no operator" + "description": "Brand buying direct" }, { "data": { "accounts": [ { "billing": "agent", - "brand_id": "dove", - "house": "unilever.com" + "brand": { + "brand_id": "spark", + "domain": "nova-brands.com" + }, + "operator": "pinnacle-media.com" }, { "billing": "agent", - "brand_id": "axe", - "house": "unilever.com" + "brand": { + "brand_id": "glow", + "domain": "nova-brands.com" + }, + "operator": "pinnacle-media.com" } ] }, @@ -59,26 +72,19 @@ "description": "An advertiser account the agent wants to operate on the seller", "properties": { "billing": { - "description": "Who should be invoiced. brand: seller invoices the brand directly. operator: seller invoices the operator (agency). agent: agent consolidates billing across brands. Omit to accept the seller's default.", + "description": "Who should be invoiced. operator: seller invoices the operator (agency or brand buying direct). agent: agent consolidates billing across brands. The seller must either accept this billing model or reject the request.", "enum": [ - "brand", "operator", "agent" ], "type": "string" }, - "brand_id": { - "description": "Brand ID within the house portfolio (from brand.json). Required when the house has multiple brands (e.g., 'dove' under unilever.com, 'tide' under pg.com). Omit for single-brand houses.", - "pattern": "^[a-z0-9_]+$", - "type": "string" - }, - "house": { - "description": "House domain where brand.json is hosted (e.g., 'unilever.com', 'acme-corp.com'). This is the canonical identity anchor for the brand, resolved via /.well-known/brand.json. For single-brand houses, this alone identifies the brand.", - "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$", - "type": "string" + "brand": { + "$ref": "../core/brand-ref.json", + "description": "Brand reference identifying the advertiser. Uses the brand's house domain and optional brand_id from brand.json." }, "operator": { - "description": "Domain of the entity operating the seat (e.g., 'groupm.com', 'mindshare.com'). Verified against the brand's authorized_operators in brand.json. Omit if the brand operates its own seat.", + "description": "Domain of the entity operating on the brand's behalf (e.g., 'pinnacle-media.com'). When the brand operates directly, this is the brand's domain. Verified against the brand's authorized_operators in brand.json.", "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$", "type": "string" }, @@ -88,7 +94,9 @@ } }, "required": [ - "house" + "brand", + "operator", + "billing" ], "type": "object" }, @@ -113,7 +121,7 @@ }, "push_notification_config": { "$ref": "../core/push-notification-config.json", - "description": "Optional webhook for async notifications when account status changes (e.g., pending_approval transitions to active)." + "description": "Webhook for async notifications when account status changes (e.g., pending_approval transitions to active)." } }, "required": [ diff --git a/schemas/cache/account/sync-accounts-response.json b/schemas/cache/account/sync-accounts-response.json index 932c2324..c5e906a0 100644 --- a/schemas/cache/account/sync-accounts-response.json +++ b/schemas/cache/account/sync-accounts-response.json @@ -6,52 +6,77 @@ "data": { "accounts": [ { - "account_id": "sub_tide_001", + "account_scope": "operator_brand", "action": "created", - "billing": "agent", - "brand_id": "tide", - "house": "pg.com", - "name": "Tide (via GroupM)", - "operator": "groupm.com", - "parent_account_id": "acc_agent_house", + "billing": "operator", + "brand": { + "brand_id": "spark", + "domain": "nova-brands.com" + }, + "name": "Spark (via Pinnacle)", + "operator": "pinnacle-media.com", "status": "active" }, { - "account_id": "acc_dove_pending", + "account_scope": "operator_brand", "action": "created", - "billing": "brand", - "brand_id": "dove", - "house": "unilever.com", - "name": "Dove", - "operator": "mindshare.com", + "billing": "operator", + "brand": { + "brand_id": "glow", + "domain": "nova-brands.com" + }, + "name": "Glow", + "operator": "pinnacle-media.com", "setup": { "expires_at": "2026-03-10T00:00:00Z", - "message": "Credit application required for direct billing", - "url": "https://seller.com/onboard/dove" + "message": "Complete advertiser registration and credit application", + "url": "https://seller.example.com/advertiser-onboard" }, "status": "pending_approval" } ] }, - "description": "Mixed results - one active, one pending approval" + "description": "Mixed results \u2014 one active, one pending approval" }, { "data": { "accounts": [ { - "account_id": "acc_agent_house", "action": "created", - "billing": "agent", - "house": "acme-corp.com", - "name": "Acme Corp (via agent)", - "status": "active", + "brand": { + "brand_id": "clearance", + "domain": "acme-corp.com" + }, + "operator": "acme-corp.com", + "status": "rejected", "warnings": [ - "Direct billing (brand) not supported. Mapped to agent billing." + "Account request declined: advertiser category not accepted on this platform." ] } ] }, - "description": "Seller doesn't support direct billing, maps to agent billing" + "description": "Rejected account \u2014 no account_id assigned" + }, + { + "data": { + "accounts": [ + { + "action": "failed", + "brand": { + "domain": "acme-corp.com" + }, + "errors": [ + { + "code": "BILLING_NOT_SUPPORTED", + "message": "Operator billing is not supported. This seller only accepts agent billing." + } + ], + "operator": "acme-corp.com", + "status": "rejected" + } + ] + }, + "description": "Unsupported billing \u2014 seller rejects the request" } ], "oneOf": [ @@ -69,8 +94,14 @@ "items": { "additionalProperties": true, "properties": { - "account_id": { - "description": "Seller-assigned account identifier. When billing is 'agent', multiple brands may share the same account_id.", + "account_scope": { + "description": "How the seller scoped this account. operator: shared across all brands for this operator. brand: shared across all operators for this brand. operator_brand: dedicated to this operator+brand pair. agent: the agent's default account.", + "enum": [ + "operator", + "brand", + "operator_brand", + "agent" + ], "type": "string" }, "action": { @@ -84,18 +115,16 @@ "type": "string" }, "billing": { - "description": "Who is invoiced on this account. May differ from the requested billing if the seller doesn't support it.", + "description": "Who is invoiced on this account. Matches the requested billing model.", "enum": [ - "brand", "operator", "agent" ], "type": "string" }, - "brand_id": { - "description": "Brand ID within the house portfolio, echoed from request", - "pattern": "^[a-z0-9_]+$", - "type": "string" + "brand": { + "$ref": "../core/brand-ref.json", + "description": "Brand reference, echoed from the request" }, "credit_limit": { "properties": { @@ -121,11 +150,6 @@ }, "type": "array" }, - "house": { - "description": "House domain, echoed from the request", - "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$", - "type": "string" - }, "name": { "description": "Human-readable account name assigned by the seller", "type": "string" @@ -134,10 +158,6 @@ "description": "Operator domain, echoed from request", "type": "string" }, - "parent_account_id": { - "description": "Parent account ID when this account is a sub-account under a shared billing account", - "type": "string" - }, "payment_terms": { "description": "Payment terms (e.g., 'net_30', 'prepay')", "type": "string" @@ -175,10 +195,11 @@ "type": "object" }, "status": { - "description": "Account status. active: ready for use. pending_approval: seller reviewing (credit, legal). payment_required: credit limit reached or funds depleted. suspended: was active, now paused. closed: terminated.", + "description": "Account status. active: ready for use. pending_approval: seller reviewing (credit, legal). rejected: seller declined the account request. payment_required: credit limit reached or funds depleted. suspended: was active, now paused. closed: was active, now terminated.", "enum": [ "active", "pending_approval", + "rejected", "payment_required", "suspended", "closed" @@ -194,7 +215,8 @@ } }, "required": [ - "house", + "brand", + "operator", "action", "status" ], diff --git a/schemas/cache/content-standards/artifact.json b/schemas/cache/content-standards/artifact.json index 8ee92049..406c4b33 100644 --- a/schemas/cache/content-standards/artifact.json +++ b/schemas/cache/content-standards/artifact.json @@ -94,6 +94,10 @@ "description": "BCP 47 language tag for this text (e.g., 'en', 'es-MX'). Useful when artifact contains mixed-language content.", "type": "string" }, + "provenance": { + "$ref": "../core/provenance.json", + "description": "Provenance for this text block, overrides artifact-level provenance" + }, "role": { "description": "Role of this text in the document. Use 'title' for the main artifact title, 'description' for summaries.", "enum": [ @@ -137,6 +141,10 @@ "description": "Image height in pixels", "type": "integer" }, + "provenance": { + "$ref": "../core/provenance.json", + "description": "Provenance for this image, overrides artifact-level provenance" + }, "type": { "const": "image", "type": "string" @@ -168,6 +176,10 @@ "description": "Video duration in milliseconds", "type": "integer" }, + "provenance": { + "$ref": "../core/provenance.json", + "description": "Provenance for this video, overrides artifact-level provenance" + }, "thumbnail_url": { "description": "Video thumbnail URL", "format": "uri", @@ -215,6 +227,10 @@ "description": "Audio duration in milliseconds", "type": "integer" }, + "provenance": { + "$ref": "../core/provenance.json", + "description": "Provenance for this audio, overrides artifact-level provenance" + }, "transcript": { "description": "Audio transcript", "type": "string" @@ -326,6 +342,10 @@ "$ref": "../core/identifier.json", "description": "Identifier for the property where this artifact appears" }, + "provenance": { + "$ref": "../core/provenance.json", + "description": "Provenance metadata for this artifact. Serves as the default provenance for all assets within this artifact \u2014 individual assets can override with their own provenance." + }, "published_time": { "description": "When the artifact was published (ISO 8601 format)", "format": "date-time", diff --git a/schemas/cache/content-standards/calibrate-content-response.json b/schemas/cache/content-standards/calibrate-content-response.json index a1a0f0be..e0a90489 100644 --- a/schemas/cache/content-standards/calibrate-content-response.json +++ b/schemas/cache/content-standards/calibrate-content-response.json @@ -11,14 +11,16 @@ "minimum": 0, "type": "number" }, - "errors": { - "description": "Field must not be present in success response", - "not": {} + "context": { + "$ref": "../core/context.json" }, "explanation": { "description": "Detailed natural language explanation of the decision", "type": "string" }, + "ext": { + "$ref": "../core/ext.json" + }, "features": { "description": "Per-feature breakdown with explanations", "items": { @@ -67,15 +69,17 @@ { "description": "Error response", "properties": { + "context": { + "$ref": "../core/context.json" + }, "errors": { "items": { "$ref": "../core/error.json" }, "type": "array" }, - "verdict": { - "description": "Field must not be present in error response", - "not": {} + "ext": { + "$ref": "../core/ext.json" } }, "required": [ diff --git a/schemas/cache/content-standards/content-standards.json b/schemas/cache/content-standards/content-standards.json index 1e5b2b86..0d07de63 100644 --- a/schemas/cache/content-standards/content-standards.json +++ b/schemas/cache/content-standards/content-standards.json @@ -57,6 +57,14 @@ "description": "Natural language policy describing acceptable and unacceptable content contexts. Used by LLMs and human reviewers to make judgments.", "type": "string" }, + "pricing_options": { + "description": "Pricing options for this content standards service. The buyer passes the selected pricing_option_id in report_usage for billing verification.", + "items": { + "$ref": "../core/pricing-option.json" + }, + "minItems": 1, + "type": "array" + }, "standards_id": { "description": "Unique identifier for this standards configuration", "type": "string" diff --git a/schemas/cache/content-standards/create-content-standards-response.json b/schemas/cache/content-standards/create-content-standards-response.json index ccc57f40..3a099c2e 100644 --- a/schemas/cache/content-standards/create-content-standards-response.json +++ b/schemas/cache/content-standards/create-content-standards-response.json @@ -8,10 +8,6 @@ "context": { "$ref": "../core/context.json" }, - "errors": { - "description": "Field must not be present in success response", - "not": {} - }, "ext": { "$ref": "../core/ext.json" }, @@ -43,10 +39,6 @@ }, "ext": { "$ref": "../core/ext.json" - }, - "standards_id": { - "description": "Field must not be present in error response", - "not": {} } }, "required": [ diff --git a/schemas/cache/content-standards/get-content-standards-response.json b/schemas/cache/content-standards/get-content-standards-response.json index 5aa29b9e..31e94903 100644 --- a/schemas/cache/content-standards/get-content-standards-response.json +++ b/schemas/cache/content-standards/get-content-standards-response.json @@ -12,10 +12,6 @@ "properties": { "context": { "$ref": "../core/context.json" - }, - "errors": { - "description": "Field must not be present in success response", - "not": {} } }, "type": "object" @@ -34,10 +30,6 @@ }, "ext": { "$ref": "../core/ext.json" - }, - "standards_id": { - "description": "Field must not be present in error response", - "not": {} } }, "required": [ diff --git a/schemas/cache/content-standards/get-media-buy-artifacts-request.json b/schemas/cache/content-standards/get-media-buy-artifacts-request.json index de24686b..f31bde4e 100644 --- a/schemas/cache/content-standards/get-media-buy-artifacts-request.json +++ b/schemas/cache/content-standards/get-media-buy-artifacts-request.json @@ -2,9 +2,9 @@ "$schema": "http://json-schema.org/draft-07/schema#", "description": "Request parameters for retrieving content artifacts from a media buy for validation", "properties": { - "account_id": { - "description": "Filter artifacts to a specific account. When provided, only returns artifacts for media buys belonging to this account. When omitted, returns artifacts across all accessible accounts. Optional if the agent has a single account.", - "type": "string" + "account": { + "$ref": "../core/account-ref.json", + "description": "Filter artifacts to a specific account. When omitted, returns artifacts across all accessible accounts." }, "context": { "$ref": "../core/context.json" diff --git a/schemas/cache/content-standards/get-media-buy-artifacts-response.json b/schemas/cache/content-standards/get-media-buy-artifacts-response.json index 72409792..66ae699c 100644 --- a/schemas/cache/content-standards/get-media-buy-artifacts-response.json +++ b/schemas/cache/content-standards/get-media-buy-artifacts-response.json @@ -69,10 +69,6 @@ "context": { "$ref": "../core/context.json" }, - "errors": { - "description": "Field must not be present in success response", - "not": {} - }, "ext": { "$ref": "../core/ext.json" }, @@ -132,10 +128,6 @@ }, "ext": { "$ref": "../core/ext.json" - }, - "media_buy_id": { - "description": "Field must not be present in error response", - "not": {} } }, "required": [ diff --git a/schemas/cache/content-standards/list-content-standards-response.json b/schemas/cache/content-standards/list-content-standards-response.json index 06a48053..5423cb7a 100644 --- a/schemas/cache/content-standards/list-content-standards-response.json +++ b/schemas/cache/content-standards/list-content-standards-response.json @@ -8,10 +8,6 @@ "context": { "$ref": "../core/context.json" }, - "errors": { - "description": "Field must not be present in success response", - "not": {} - }, "ext": { "$ref": "../core/ext.json" }, @@ -45,10 +41,6 @@ }, "ext": { "$ref": "../core/ext.json" - }, - "standards": { - "description": "Field must not be present in error response", - "not": {} } }, "required": [ diff --git a/schemas/cache/content-standards/update-content-standards-response.json b/schemas/cache/content-standards/update-content-standards-response.json index e160948a..56aa8f3f 100644 --- a/schemas/cache/content-standards/update-content-standards-response.json +++ b/schemas/cache/content-standards/update-content-standards-response.json @@ -1,30 +1,67 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": true, "description": "Response from updating a content standards configuration", - "properties": { - "conflicting_standards_id": { - "description": "If scope change conflicts with another configuration, the ID of the conflicting standards", - "type": "string" - }, - "context": { - "$ref": "../core/context.json" - }, - "errors": { - "description": "Errors that occurred during the update", - "items": { - "$ref": "../core/error.json" + "oneOf": [ + { + "additionalProperties": true, + "properties": { + "context": { + "$ref": "../core/context.json" + }, + "ext": { + "$ref": "../core/ext.json" + }, + "standards_id": { + "description": "ID of the updated standards configuration", + "type": "string" + }, + "success": { + "const": true, + "description": "Indicates the update was applied successfully", + "type": "boolean" + } }, - "type": "array" + "required": [ + "success", + "standards_id" + ], + "title": "UpdateContentStandardsSuccess", + "type": "object" }, - "ext": { - "$ref": "../core/ext.json" - }, - "standards_id": { - "description": "ID of the updated standards configuration", - "type": "string" + { + "additionalProperties": true, + "properties": { + "conflicting_standards_id": { + "description": "If scope change conflicts with another configuration, the ID of the conflicting standards", + "type": "string" + }, + "context": { + "$ref": "../core/context.json" + }, + "errors": { + "description": "Errors that occurred during the update", + "items": { + "$ref": "../core/error.json" + }, + "minItems": 1, + "type": "array" + }, + "ext": { + "$ref": "../core/ext.json" + }, + "success": { + "const": false, + "description": "Indicates the update failed", + "type": "boolean" + } + }, + "required": [ + "success", + "errors" + ], + "title": "UpdateContentStandardsError", + "type": "object" } - }, - "title": "Update Content Standards Response", - "type": "object" + ], + "title": "Update Content Standards Response" } \ No newline at end of file diff --git a/schemas/cache/content-standards/validate-content-delivery-response.json b/schemas/cache/content-standards/validate-content-delivery-response.json index 864b258a..3c322463 100644 --- a/schemas/cache/content-standards/validate-content-delivery-response.json +++ b/schemas/cache/content-standards/validate-content-delivery-response.json @@ -8,10 +8,6 @@ "context": { "$ref": "../core/context.json" }, - "errors": { - "description": "Field must not be present in success response", - "not": {} - }, "ext": { "$ref": "../core/ext.json" }, @@ -114,10 +110,6 @@ }, "ext": { "$ref": "../core/ext.json" - }, - "summary": { - "description": "Field must not be present in error response", - "not": {} } }, "required": [ diff --git a/schemas/cache/core/account-ref.json b/schemas/cache/core/account-ref.json new file mode 100644 index 00000000..f1f916ee --- /dev/null +++ b/schemas/cache/core/account-ref.json @@ -0,0 +1,56 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Reference to an account by seller-assigned ID or natural key. Use account_id when the buyer manages accounts (e.g., picked from list_accounts). Use the natural key (brand + operator) when the seller resolves accounts internally.", + "examples": [ + { + "account_id": "acc_acme_001" + }, + { + "brand": { + "domain": "acme-corp.com" + }, + "operator": "acme-corp.com" + }, + { + "brand": { + "brand_id": "spark", + "domain": "nova-brands.com" + }, + "operator": "pinnacle-media.com" + } + ], + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "account_id": { + "description": "Seller-assigned account identifier (from sync_accounts or list_accounts)", + "type": "string" + } + }, + "required": [ + "account_id" + ] + }, + { + "additionalProperties": false, + "properties": { + "brand": { + "$ref": "brand-ref.json", + "description": "Brand reference identifying the advertiser" + }, + "operator": { + "description": "Domain of the entity operating on the brand's behalf. When the brand operates directly, this is the brand's domain.", + "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$", + "type": "string" + } + }, + "required": [ + "brand", + "operator" + ] + } + ], + "title": "Account Reference", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/core/account.json b/schemas/cache/core/account.json index 6dfc65dd..acaedcb6 100644 --- a/schemas/cache/core/account.json +++ b/schemas/cache/core/account.json @@ -6,8 +6,14 @@ { "data": { "account_id": "acc_acme_direct", + "account_scope": "brand", "advertiser": "Acme Corp", + "billing": "operator", + "brand": { + "domain": "acme-corp.com" + }, "name": "Acme", + "operator": "acme-corp.com", "payment_terms": "net_30", "rate_card": "acme_vip_2024", "status": "active" @@ -17,9 +23,15 @@ { "data": { "account_id": "acc_acme_pinnacle", + "account_scope": "operator_brand", "advertiser": "Acme Corp", + "billing": "operator", "billing_proxy": "Pinnacle Media", + "brand": { + "domain": "acme-corp.com" + }, "name": "Acme c/o Pinnacle", + "operator": "pinnacle-media.com", "payment_terms": "net_60", "rate_card": "acme_vip_2024", "status": "active" @@ -29,8 +41,14 @@ { "data": { "account_id": "acc_pinnacle", + "account_scope": "operator", "advertiser": "Pinnacle Media", + "billing": "operator", + "brand": { + "domain": "pinnacle-media.com" + }, "name": "Pinnacle", + "operator": "pinnacle-media.com", "payment_terms": "net_45", "rate_card": "agency_standard", "status": "active" @@ -39,13 +57,16 @@ }, { "data": { - "account_id": "sub_dove_001", - "advertiser": "Unilever", + "account_id": "acc_spark_001", + "account_scope": "operator_brand", + "advertiser": "Nova Brands", "billing": "agent", - "brand_id": "dove", - "house": "unilever.com", - "name": "Dove (via GroupM)", - "operator": "groupm.com", + "brand": { + "brand_id": "spark", + "domain": "nova-brands.com" + }, + "name": "Spark (via Pinnacle)", + "operator": "pinnacle-media.com", "payment_terms": "net_30", "status": "active" }, @@ -53,13 +74,16 @@ }, { "data": { - "account_id": "acc_axe_pending", - "advertiser": "Unilever", - "billing": "brand", - "brand_id": "axe", - "house": "unilever.com", - "name": "Axe", - "operator": "mindshare.com", + "account_id": "acc_glow_pending", + "account_scope": "brand", + "advertiser": "Nova Brands", + "billing": "operator", + "brand": { + "brand_id": "glow", + "domain": "nova-brands.com" + }, + "name": "Glow", + "operator": "pinnacle-media.com", "status": "pending_approval" }, "description": "Pending account awaiting seller approval" @@ -70,14 +94,23 @@ "description": "Unique identifier for this account", "type": "string" }, + "account_scope": { + "description": "How the seller scoped this account. operator: shared across all brands for this operator. brand: shared across all operators for this brand. operator_brand: dedicated to a specific operator+brand combination. agent: the agent's default account with no brand or operator association.", + "enum": [ + "operator", + "brand", + "operator_brand", + "agent" + ], + "type": "string" + }, "advertiser": { "description": "The advertiser whose rates apply to this account", "type": "string" }, "billing": { - "description": "Who is invoiced on this account. brand: seller invoices the brand directly. operator: seller invoices the operator (agency). agent: agent consolidates billing.", + "description": "Who is invoiced on this account. operator: seller invoices the operator (agency or brand buying direct). agent: agent consolidates billing.", "enum": [ - "brand", "operator", "agent" ], @@ -87,10 +120,9 @@ "description": "Optional intermediary who receives invoices on behalf of the advertiser (e.g., agency)", "type": "string" }, - "brand_id": { - "description": "Brand ID within the house portfolio (from brand.json)", - "pattern": "^[a-z0-9_]+$", - "type": "string" + "brand": { + "$ref": "brand-ref.json", + "description": "Brand reference identifying the advertiser" }, "credit_limit": { "description": "Maximum outstanding balance allowed", @@ -113,17 +145,12 @@ "ext": { "$ref": "ext.json" }, - "house": { - "description": "House domain where brand.json is hosted. Canonical identity anchor for the brand.", - "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$", - "type": "string" - }, "name": { "description": "Human-readable account name (e.g., 'Acme', 'Acme c/o Pinnacle')", "type": "string" }, "operator": { - "description": "Domain of the entity operating this account", + "description": "Domain of the entity operating this account. When the brand operates directly, this is the brand's domain.", "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$", "type": "string" }, @@ -139,11 +166,36 @@ "description": "When true, this is a sandbox account. All requests using this account_id are treated as sandbox \u2014 no real platform calls, no real spend.", "type": "boolean" }, + "setup": { + "additionalProperties": true, + "description": "Present when status is 'pending_approval'. Contains next steps for completing account activation.", + "properties": { + "expires_at": { + "description": "When this setup link expires.", + "format": "date-time", + "type": "string" + }, + "message": { + "description": "Human-readable description of what's needed.", + "type": "string" + }, + "url": { + "description": "URL where the human can complete the required action (credit application, legal agreement, add funds).", + "format": "uri", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, "status": { - "description": "Account status. pending_approval: seller reviewing (credit, contracts). payment_required: credit limit reached or funds depleted. suspended: was active, now paused. closed: terminated.", + "description": "Account status. pending_approval: seller reviewing (credit, contracts). rejected: seller declined the account request. payment_required: credit limit reached or funds depleted. suspended: was active, now paused. closed: was active, now terminated.", "enum": [ "active", "pending_approval", + "rejected", "payment_required", "suspended", "closed" diff --git a/schemas/cache/core/assets/audio-asset.json b/schemas/cache/core/assets/audio-asset.json index 753f81b9..24eeda8a 100644 --- a/schemas/cache/core/assets/audio-asset.json +++ b/schemas/cache/core/assets/audio-asset.json @@ -49,6 +49,10 @@ "description": "Integrated loudness in LUFS", "type": "number" }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" + }, "sampling_rate_hz": { "description": "Sampling rate in Hz (e.g., 44100, 48000, 96000)", "type": "integer" diff --git a/schemas/cache/core/assets/brief-asset.json b/schemas/cache/core/assets/brief-asset.json new file mode 100644 index 00000000..9d4caa4a --- /dev/null +++ b/schemas/cache/core/assets/brief-asset.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "allOf": [ + { + "$ref": "../../core/creative-brief.json" + } + ], + "description": "Campaign-level creative context as an asset. Carries the creative brief through the manifest so it travels with the creative through regeneration, resizing, and auditing.", + "title": "Brief Asset" +} \ No newline at end of file diff --git a/schemas/cache/core/assets/catalog-asset.json b/schemas/cache/core/assets/catalog-asset.json new file mode 100644 index 00000000..8f44df83 --- /dev/null +++ b/schemas/cache/core/assets/catalog-asset.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "allOf": [ + { + "$ref": "../../core/catalog.json" + } + ], + "description": "A typed data feed as a creative asset. Carries catalog context (products, stores, jobs, etc.) within the manifest's assets map.", + "title": "Catalog Asset" +} \ No newline at end of file diff --git a/schemas/cache/core/assets/css-asset.json b/schemas/cache/core/assets/css-asset.json index 2327c516..094a58e5 100644 --- a/schemas/cache/core/assets/css-asset.json +++ b/schemas/cache/core/assets/css-asset.json @@ -10,6 +10,10 @@ "media": { "description": "CSS media query context (e.g., 'screen', 'print')", "type": "string" + }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" } }, "required": [ diff --git a/schemas/cache/core/assets/daast-asset.json b/schemas/cache/core/assets/daast-asset.json index acd30ded..2e2f860c 100644 --- a/schemas/cache/core/assets/daast-asset.json +++ b/schemas/cache/core/assets/daast-asset.json @@ -23,6 +23,10 @@ "minimum": 0, "type": "integer" }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" + }, "tracking_events": { "description": "Tracking events supported by this DAAST tag", "items": { @@ -73,6 +77,10 @@ "minimum": 0, "type": "integer" }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" + }, "tracking_events": { "description": "Tracking events supported by this DAAST tag", "items": { diff --git a/schemas/cache/core/assets/html-asset.json b/schemas/cache/core/assets/html-asset.json index 27ea7a46..fd5a718e 100644 --- a/schemas/cache/core/assets/html-asset.json +++ b/schemas/cache/core/assets/html-asset.json @@ -30,6 +30,10 @@ "description": "HTML content", "type": "string" }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" + }, "version": { "description": "HTML version (e.g., 'HTML5')", "type": "string" diff --git a/schemas/cache/core/assets/image-asset.json b/schemas/cache/core/assets/image-asset.json index 5d13743b..ee5dcb0a 100644 --- a/schemas/cache/core/assets/image-asset.json +++ b/schemas/cache/core/assets/image-asset.json @@ -17,6 +17,10 @@ "minimum": 1, "type": "integer" }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" + }, "url": { "description": "URL to the image asset", "format": "uri", diff --git a/schemas/cache/core/assets/javascript-asset.json b/schemas/cache/core/assets/javascript-asset.json index 8fa6ea09..0267b7ac 100644 --- a/schemas/cache/core/assets/javascript-asset.json +++ b/schemas/cache/core/assets/javascript-asset.json @@ -33,6 +33,10 @@ "module_type": { "$ref": "../../enums/javascript-module-type.json", "description": "JavaScript module type" + }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" } }, "required": [ diff --git a/schemas/cache/core/assets/text-asset.json b/schemas/cache/core/assets/text-asset.json index 7f4b2d40..2d12e062 100644 --- a/schemas/cache/core/assets/text-asset.json +++ b/schemas/cache/core/assets/text-asset.json @@ -10,6 +10,10 @@ "language": { "description": "Language code (e.g., 'en', 'es', 'fr')", "type": "string" + }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" } }, "required": [ diff --git a/schemas/cache/core/assets/url-asset.json b/schemas/cache/core/assets/url-asset.json index ac5ea5bb..733ff2b8 100644 --- a/schemas/cache/core/assets/url-asset.json +++ b/schemas/cache/core/assets/url-asset.json @@ -7,6 +7,10 @@ "description": "Description of what this URL points to", "type": "string" }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" + }, "url": { "description": "URL reference", "format": "uri", diff --git a/schemas/cache/core/assets/vast-asset.json b/schemas/cache/core/assets/vast-asset.json index 768a5a47..566e4ca4 100644 --- a/schemas/cache/core/assets/vast-asset.json +++ b/schemas/cache/core/assets/vast-asset.json @@ -27,6 +27,10 @@ "minimum": 0, "type": "integer" }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" + }, "tracking_events": { "description": "Tracking events supported by this VAST tag", "items": { @@ -83,6 +87,10 @@ "minimum": 0, "type": "integer" }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" + }, "tracking_events": { "description": "Tracking events supported by this VAST tag", "items": { diff --git a/schemas/cache/core/assets/video-asset.json b/schemas/cache/core/assets/video-asset.json index d0836831..394e84aa 100644 --- a/schemas/cache/core/assets/video-asset.json +++ b/schemas/cache/core/assets/video-asset.json @@ -141,6 +141,10 @@ ], "type": "string" }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" + }, "scan_type": { "description": "Scan type of the video", "enum": [ diff --git a/schemas/cache/core/assets/webhook-asset.json b/schemas/cache/core/assets/webhook-asset.json index aec81a6c..58e923a5 100644 --- a/schemas/cache/core/assets/webhook-asset.json +++ b/schemas/cache/core/assets/webhook-asset.json @@ -8,6 +8,10 @@ "default": "POST", "description": "HTTP method" }, + "provenance": { + "$ref": "../../core/provenance.json", + "description": "Provenance metadata for this asset, overrides manifest-level provenance" + }, "required_macros": { "description": "Universal macros that must be provided for webhook to function", "items": { diff --git a/schemas/cache/core/attribution-window.json b/schemas/cache/core/attribution-window.json index 0a48b529..6dd1e3be 100644 --- a/schemas/cache/core/attribution-window.json +++ b/schemas/cache/core/attribution-window.json @@ -3,19 +3,25 @@ "additionalProperties": true, "description": "Describes the attribution methodology and lookback windows used for conversion measurement. Enables cross-platform comparison by making attribution methodology transparent.", "properties": { - "click_window_days": { - "description": "Click-through attribution window in days. Conversions occurring within this many days after a click are attributed to the ad.", - "minimum": 0, - "type": "integer" - }, "model": { "$ref": "../enums/attribution-model.json", "description": "Attribution model used to assign credit when multiple touchpoints exist" }, - "view_window_days": { - "description": "View-through attribution window in days. Conversions occurring within this many days after an ad impression (without click) are attributed to the ad.", - "minimum": 0, - "type": "integer" + "post_click": { + "allOf": [ + { + "$ref": "duration.json" + } + ], + "description": "Post-click attribution window. Conversions occurring within this duration after a click are attributed to the ad." + }, + "post_view": { + "allOf": [ + { + "$ref": "duration.json" + } + ], + "description": "Post-view attribution window. Conversions occurring within this duration after an ad impression (without click) are attributed to the ad." } }, "required": [ diff --git a/schemas/cache/core/audience-member.json b/schemas/cache/core/audience-member.json index 78747484..bb7d33e6 100644 --- a/schemas/cache/core/audience-member.json +++ b/schemas/cache/core/audience-member.json @@ -18,11 +18,15 @@ ] } ], - "description": "Hashed identifiers for a CRM audience member. All identifiers must be normalized before hashing: emails to lowercase+trim, phone numbers to E.164 format (e.g. +12065551234). At least one identifier is required. Providing multiple identifiers for the same person improves match rates. Composite identifiers (e.g. hashed first name + last name + zip for Google Customer Match) are not yet standardized \u2014 use the ext field for platform-specific extensions.", + "description": "A CRM audience member identified by a buyer-assigned external_id and at least one matchable identifier. All identifiers must be normalized before hashing: emails to lowercase+trim, phone numbers to E.164 format (e.g. +12065551234). Providing multiple identifiers for the same person improves match rates. Composite identifiers (e.g. hashed first name + last name + zip for Google Customer Match) are not yet standardized \u2014 use the ext field for platform-specific extensions.", "properties": { "ext": { "$ref": "ext.json" }, + "external_id": { + "description": "Buyer-assigned stable identifier for this audience member (e.g. CRM record ID, loyalty ID). Used for deduplication, removal, and cross-referencing with buyer systems. Adapters for CDPs that don't natively assign IDs can derive one (e.g. hash of the member's identifiers).", + "type": "string" + }, "hashed_email": { "description": "SHA-256 hash of lowercase, trimmed email address.", "pattern": "^[a-f0-9]{64}$", @@ -57,6 +61,9 @@ "type": "array" } }, + "required": [ + "external_id" + ], "title": "Audience Member", "type": "object" } \ No newline at end of file diff --git a/schemas/cache/core/brand-ref.json b/schemas/cache/core/brand-ref.json index 2161b35a..a32c0c7b 100644 --- a/schemas/cache/core/brand-ref.json +++ b/schemas/cache/core/brand-ref.json @@ -4,15 +4,15 @@ "description": "Reference to a brand by domain and optional brand_id. The domain hosts /.well-known/brand.json or is registered in the brand registry. For single-brand domains, brand_id can be omitted. For house-of-brands domains, brand_id identifies the specific brand.", "examples": [ { - "brand_id": "tide", - "domain": "pg.com" + "brand_id": "spark", + "domain": "nova-brands.com" }, { - "brand_id": "air_jordan", - "domain": "nikeinc.com" + "brand_id": "glow", + "domain": "nova-brands.com" }, { - "domain": "bobsburgers.com" + "domain": "acme-corp.com" } ], "properties": { diff --git a/schemas/cache/core/catalog-field-mapping.json b/schemas/cache/core/catalog-field-mapping.json new file mode 100644 index 00000000..8c732fbe --- /dev/null +++ b/schemas/cache/core/catalog-field-mapping.json @@ -0,0 +1,143 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "allOf": [ + { + "not": { + "required": [ + "feed_field", + "value" + ] + } + }, + { + "not": { + "required": [ + "catalog_field", + "asset_group_id" + ] + } + } + ], + "description": "Declares how a field in an external feed maps to the AdCP catalog item schema. Used in sync_catalogs feed_field_mappings to normalize non-AdCP feeds (Google Merchant Center, LinkedIn Jobs XML, hotel XML, etc.) to the standard catalog item schema without requiring the buyer to preprocess every feed. Multiple mappings can assemble a nested object via dot notation (e.g., separate mappings for price.amount and price.currency).", + "examples": [ + { + "data": { + "catalog_field": "name", + "feed_field": "hotel_name" + }, + "description": "Simple field rename" + }, + { + "data": { + "catalog_field": "valid_from", + "feed_field": "available_from", + "format": "YYYYMMDD", + "timezone": "UTC", + "transform": "date" + }, + "description": "Date format transform \u2014 YYYYMMDD to ISO 8601" + }, + { + "data": { + "by": 100, + "catalog_field": "price.amount", + "feed_field": "price_cents", + "transform": "divide" + }, + "description": "Divide cents to dollars" + }, + { + "data": { + "catalog_field": "price.currency", + "value": "USD" + }, + "description": "Static literal injection \u2014 currency not in feed" + }, + { + "data": { + "asset_group_id": "images_landscape", + "feed_field": "primary_photo_url" + }, + "description": "Image URL assigned to typed asset pool" + }, + { + "data": { + "asset_group_id": "images_vertical", + "feed_field": "portrait_photo_url" + }, + "description": "Vertical photo URL assigned to vertical pool" + }, + { + "data": { + "catalog_field": "amenities", + "feed_field": "amenity_list", + "separator": ",", + "transform": "split" + }, + "description": "Split comma-separated tags to array" + }, + { + "data": { + "catalog_field": "available", + "default": false, + "feed_field": "is_available", + "transform": "boolean" + }, + "description": "Boolean coercion with default" + } + ], + "properties": { + "asset_group_id": { + "description": "Places the feed field value (a URL) into a typed asset pool on the catalog item's assets array. The value is wrapped as an image or video asset in a group with this ID. Use standard group IDs: 'images_landscape', 'images_vertical', 'images_square', 'logo', 'video'. Mutually exclusive with catalog_field.", + "type": "string" + }, + "by": { + "description": "For transform 'divide': the divisor to apply (e.g., 100 to convert integer cents to decimal dollars).", + "exclusiveMinimum": 0, + "type": "number" + }, + "catalog_field": { + "description": "Target field on the catalog item schema, using dot notation for nested fields (e.g., 'name', 'price.amount', 'location.city'). Mutually exclusive with asset_group_id.", + "type": "string" + }, + "default": { + "description": "Fallback value to use when feed_field is absent, null, or empty. Applied after any transform would have been applied. Allows optional feed fields to have a guaranteed baseline value." + }, + "ext": { + "$ref": "ext.json" + }, + "feed_field": { + "description": "Field name in the external feed record. Omit when injecting a static literal value (use the value property instead).", + "type": "string" + }, + "format": { + "description": "For transform 'date': the input date format string (e.g., 'YYYYMMDD', 'MM/DD/YYYY', 'DD-MM-YYYY'). Output is always ISO 8601 (e.g., '2025-03-01'). Uses Unicode date pattern tokens.", + "type": "string" + }, + "separator": { + "default": ",", + "description": "For transform 'split': the separator character or string to split on. Defaults to ','.", + "type": "string" + }, + "timezone": { + "description": "For transform 'date': the timezone of the input value. IANA timezone identifier (e.g., 'UTC', 'America/New_York', 'Europe/Amsterdam'). Defaults to UTC when omitted.", + "type": "string" + }, + "transform": { + "description": "Named transform to apply to the feed field value before writing to the catalog schema. See transform-specific parameters (format, timezone, by, separator).", + "enum": [ + "date", + "divide", + "boolean", + "split" + ], + "type": "string" + }, + "value": { + "description": "Static literal value to inject into catalog_field for every item, regardless of what the feed contains. Mutually exclusive with feed_field. Useful for fields the feed omits (e.g., currency when price is always USD, or a constant category value)." + } + }, + "title": "Catalog Field Mapping", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/core/catalog.json b/schemas/cache/core/catalog.json index 06d2e0f2..5b4ca34b 100644 --- a/schemas/cache/core/catalog.json +++ b/schemas/cache/core/catalog.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/core/catalog.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "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, app). Selectors (ids, tags, category, query) filter items regardless of sourcing method.", @@ -134,20 +133,28 @@ "type": "string" }, "content_id_type": { - "$ref": "/schemas/latest/enums/content-id-type.json", + "$ref": "../enums/content-id-type.json", "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." }, "conversion_events": { "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.", "items": { - "$ref": "/schemas/latest/enums/event-type.json" + "$ref": "../enums/event-type.json" }, "minItems": 1, "type": "array", "uniqueItems": true }, + "feed_field_mappings": { + "description": "Declarative normalization rules for external feeds. Maps non-standard feed field names, date formats, price encodings, and image URLs to the AdCP catalog item schema. Applied during sync_catalogs ingestion. Supports field renames, named transforms (date, divide, boolean, split), static literal injection, and assignment of image URLs to typed asset pools.", + "items": { + "$ref": "catalog-field-mapping.json" + }, + "minItems": 1, + "type": "array" + }, "feed_format": { - "$ref": "/schemas/latest/enums/feed-format.json", + "$ref": "../enums/feed-format.json", "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." }, "gtins": { @@ -192,11 +199,11 @@ "type": "array" }, "type": { - "$ref": "/schemas/latest/enums/catalog-type.json", + "$ref": "../enums/catalog-type.json", "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', 'app' \u2014 each with an industry-specific item schema." }, "update_frequency": { - "$ref": "/schemas/latest/enums/update-frequency.json", + "$ref": "../enums/update-frequency.json", "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." }, "url": { diff --git a/schemas/cache/core/catchment.json b/schemas/cache/core/catchment.json index 7373952e..80cef812 100644 --- a/schemas/cache/core/catchment.json +++ b/schemas/cache/core/catchment.json @@ -146,7 +146,8 @@ "description": "Pre-computed GeoJSON geometry defining the catchment boundary. Use this when the buyer has already calculated isochrones (via TravelTime, Mapbox, etc.) or has custom trade area boundaries. Supports Polygon and MultiPolygon types.", "properties": { "coordinates": { - "description": "GeoJSON coordinates array. For Polygon: array of linear rings. For MultiPolygon: array of polygons." + "description": "GeoJSON coordinates array. For Polygon: array of linear rings. For MultiPolygon: array of polygons.", + "type": "array" }, "type": { "description": "GeoJSON geometry type.", diff --git a/schemas/cache/core/creative-asset.json b/schemas/cache/core/creative-asset.json index 2dbe7ea7..0daca8ed 100644 --- a/schemas/cache/core/creative-asset.json +++ b/schemas/cache/core/creative-asset.json @@ -1,64 +1,67 @@ { - "$id": "/schemas/latest/core/creative-asset.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Creative asset for upload to library - supports static assets, generative formats, and third-party snippets", "properties": { "assets": { "additionalProperties": true, - "description": "Assets required by the format, keyed by asset_role", + "description": "Assets required by the format, keyed by asset_id", "patternProperties": { - "^[a-zA-Z0-9_-]+$": { - "oneOf": [ + "^[a-z0-9_]+$": { + "anyOf": [ { - "$ref": "/schemas/latest/core/assets/image-asset.json" + "$ref": "assets/image-asset.json" }, { - "$ref": "/schemas/latest/core/assets/video-asset.json" + "$ref": "assets/video-asset.json" }, { - "$ref": "/schemas/latest/core/assets/audio-asset.json" + "$ref": "assets/audio-asset.json" }, { - "$ref": "/schemas/latest/core/assets/text-asset.json" + "$ref": "assets/vast-asset.json" }, { - "$ref": "/schemas/latest/core/assets/html-asset.json" + "$ref": "assets/text-asset.json" }, { - "$ref": "/schemas/latest/core/assets/css-asset.json" + "$ref": "assets/url-asset.json" }, { - "$ref": "/schemas/latest/core/assets/javascript-asset.json" + "$ref": "assets/html-asset.json" }, { - "$ref": "/schemas/latest/core/assets/vast-asset.json" + "$ref": "assets/javascript-asset.json" }, { - "$ref": "/schemas/latest/core/assets/daast-asset.json" + "$ref": "assets/webhook-asset.json" }, { - "$ref": "/schemas/latest/core/assets/url-asset.json" + "$ref": "assets/css-asset.json" + }, + { + "$ref": "assets/daast-asset.json" + }, + { + "$ref": "assets/markdown-asset.json" + }, + { + "$ref": "assets/brief-asset.json" + }, + { + "$ref": "assets/catalog-asset.json" } ] } }, "type": "object" }, - "catalogs": { - "description": "Catalogs this creative renders. Each entry satisfies one of the format's catalog_requirements, matched by type. Each catalog can be inline (with items), a reference to a synced catalog (by catalog_id), or a URL to an external feed.", - "items": { - "$ref": "/schemas/latest/core/catalog.json" - }, - "minItems": 1, - "type": "array" - }, "creative_id": { "description": "Unique identifier for the creative", "type": "string" }, "format_id": { - "$ref": "/schemas/latest/core/format-id.json", + "$ref": "format-id.json", "description": "Format identifier specifying which format this creative conforms to. Can be: (1) concrete format_id referencing a format with fixed dimensions, (2) template format_id referencing a template format, or (3) parameterized format_id with dimensions/duration parameters for template formats." }, "inputs": { @@ -101,8 +104,12 @@ "minItems": 1, "type": "array" }, + "provenance": { + "$ref": "provenance.json", + "description": "Provenance metadata for this creative. Serves as the default provenance for all manifests and assets within this creative. A manifest or asset with its own provenance replaces this object entirely (no field-level merging)." + }, "status": { - "$ref": "/schemas/latest/enums/creative-status.json", + "$ref": "../enums/creative-status.json", "description": "For generative creatives: set to 'approved' to finalize, 'rejected' to request regeneration with updated assets/message. Omit for non-generative creatives (system will set based on processing state)." }, "tags": { diff --git a/schemas/cache/core/creative-assignment.json b/schemas/cache/core/creative-assignment.json index 5ddea189..fbc8f4c9 100644 --- a/schemas/cache/core/creative-assignment.json +++ b/schemas/cache/core/creative-assignment.json @@ -16,7 +16,7 @@ "type": "array" }, "weight": { - "description": "Delivery weight for this creative", + "description": "Relative delivery weight for this creative (0\u2013100). When multiple creatives are assigned to the same package, weights determine impression distribution proportionally \u2014 a creative with weight 2 gets twice the delivery of weight 1. When omitted, the creative receives equal rotation with other unweighted creatives. A weight of 0 means the creative is assigned but paused (receives no delivery).", "maximum": 100, "minimum": 0, "type": "number" diff --git a/schemas/cache/core/creative-brief-ref.json b/schemas/cache/core/creative-brief-ref.json deleted file mode 100644 index 547679d0..00000000 --- a/schemas/cache/core/creative-brief-ref.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "description": "Creative brief provided either as an inline object or a URL string pointing to a hosted brief", - "examples": [ - { - "data": { - "audience": "Gift shoppers aged 25-55", - "messaging": { - "cta": "Shop Holiday Deals", - "headline": "Give the Gift of Style" - }, - "name": "Holiday Campaign 2025", - "objective": "conversion" - }, - "description": "Inline creative brief" - }, - { - "data": "https://cdn.example.com/briefs/holiday-2025.json", - "description": "URL string reference to hosted brief" - } - ], - "oneOf": [ - { - "$ref": "creative-brief.json", - "description": "Inline creative brief object" - }, - { - "description": "URL to a hosted creative brief JSON file. The brief at this URL must conform to the creative-brief.json schema.", - "format": "uri", - "type": "string" - } - ], - "title": "Creative Brief Reference" -} \ No newline at end of file diff --git a/schemas/cache/core/creative-brief.json b/schemas/cache/core/creative-brief.json index c7cb34b4..fd24d7e7 100644 --- a/schemas/cache/core/creative-brief.json +++ b/schemas/cache/core/creative-brief.json @@ -7,6 +7,65 @@ "description": "Target audience description for this campaign", "type": "string" }, + "compliance": { + "additionalProperties": true, + "description": "Regulatory and legal compliance requirements for this campaign. Campaign-specific, regional, and product-based \u2014 distinct from brand-level disclaimers in brand.json.", + "properties": { + "prohibited_claims": { + "description": "Claims that must not appear in creatives for this campaign. Creative agents should ensure generated content avoids these claims.", + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "required_disclosures": { + "description": "Disclosures that must appear in creatives for this campaign. Each disclosure specifies the text, where it should appear, and which jurisdictions require it.", + "items": { + "additionalProperties": true, + "properties": { + "jurisdictions": { + "description": "Jurisdictions where this disclosure is required. ISO 3166-1 alpha-2 country codes or ISO 3166-2 subdivision codes (e.g., 'US', 'GB', 'US-NJ', 'CA-QC'). If omitted, the disclosure applies to all jurisdictions in the campaign.", + "items": { + "pattern": "^[A-Z]{2}(-[A-Z0-9]{1,3})?$", + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "language": { + "description": "Language of the disclosure text as a BCP 47 language tag (e.g., 'en', 'fr-CA', 'es'). When omitted, the disclosure is assumed to match the creative's language.", + "type": "string" + }, + "min_duration_ms": { + "description": "Minimum display duration in milliseconds. For video/audio disclosures, how long the disclosure must be visible or audible. For static formats, how long the disclosure must remain on screen before any auto-advance.", + "minimum": 1, + "type": "integer" + }, + "position": { + "$ref": "../enums/disclosure-position.json", + "description": "Where the disclosure should appear within the creative. prominent: clearly visible in the main creative area; footer: at the bottom of visual creatives; audio: spoken in audio/video creatives; subtitle: displayed in the subtitle or closed-caption track; overlay: superimposed on video content; end_card: displayed on video end card; pre_roll: spoken or displayed before main content; companion: in companion ad unit alongside primary creative" + }, + "regulation": { + "description": "The regulation or legal authority requiring this disclosure (e.g., 'SEC Rule 156', 'FCA COBS 4.5', 'FDA 21 CFR 202')", + "type": "string" + }, + "text": { + "description": "The disclosure text that must appear in the creative", + "type": "string" + } + }, + "required": [ + "text" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + } + }, + "type": "object" + }, "messaging": { "additionalProperties": true, "description": "Messaging framework for the campaign", diff --git a/schemas/cache/core/creative-filters.json b/schemas/cache/core/creative-filters.json index 78cb7e03..7b86b8ab 100644 --- a/schemas/cache/core/creative-filters.json +++ b/schemas/cache/core/creative-filters.json @@ -3,10 +3,10 @@ "additionalProperties": true, "description": "Filter criteria for querying creative assets from the centralized library. By default, archived creatives are excluded from results. To include archived creatives, explicitly filter by status='archived' or include 'archived' in the statuses array.", "properties": { - "account_ids": { + "accounts": { "description": "Filter creatives by owning accounts. Useful for agencies managing multiple client accounts.", "items": { - "type": "string" + "$ref": "account-ref.json" }, "minItems": 1, "type": "array" diff --git a/schemas/cache/core/creative-manifest.json b/schemas/cache/core/creative-manifest.json index d26a36eb..5b7585e1 100644 --- a/schemas/cache/core/creative-manifest.json +++ b/schemas/cache/core/creative-manifest.json @@ -1,8 +1,7 @@ { - "$id": "/schemas/latest/core/creative-manifest.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, - "description": "Complete specification of a creative with all assets needed for rendering in a specific format. Each asset is typed according to its asset_role from the format specification and contains the actual content/URL that fulfills the format requirements.", + "description": "Complete specification of a creative: format_id + assets. Everything the creative needs \u2014 images, text, briefs, catalogs \u2014 lives in the assets map, declared by the format. Each asset is typed according to its asset_type from the format specification.", "properties": { "assets": { "additionalProperties": true, @@ -11,57 +10,62 @@ "^[a-z0-9_]+$": { "anyOf": [ { - "$ref": "/schemas/latest/core/assets/image-asset.json" + "$ref": "assets/image-asset.json" }, { - "$ref": "/schemas/latest/core/assets/video-asset.json" + "$ref": "assets/video-asset.json" }, { - "$ref": "/schemas/latest/core/assets/audio-asset.json" + "$ref": "assets/audio-asset.json" }, { - "$ref": "/schemas/latest/core/assets/vast-asset.json" + "$ref": "assets/vast-asset.json" }, { - "$ref": "/schemas/latest/core/assets/text-asset.json" + "$ref": "assets/text-asset.json" }, { - "$ref": "/schemas/latest/core/assets/url-asset.json" + "$ref": "assets/url-asset.json" }, { - "$ref": "/schemas/latest/core/assets/html-asset.json" + "$ref": "assets/html-asset.json" }, { - "$ref": "/schemas/latest/core/assets/javascript-asset.json" + "$ref": "assets/javascript-asset.json" }, { - "$ref": "/schemas/latest/core/assets/webhook-asset.json" + "$ref": "assets/webhook-asset.json" }, { - "$ref": "/schemas/latest/core/assets/css-asset.json" + "$ref": "assets/css-asset.json" }, { - "$ref": "/schemas/latest/core/assets/daast-asset.json" + "$ref": "assets/daast-asset.json" + }, + { + "$ref": "assets/markdown-asset.json" + }, + { + "$ref": "assets/brief-asset.json" + }, + { + "$ref": "assets/catalog-asset.json" } ] } }, "type": "object" }, - "catalogs": { - "description": "Catalogs this creative renders. Each entry satisfies one of the format's catalog_requirements, matched by type. Tells the creative what data to display \u2014 product listings for a carousel, job vacancies for a recruitment ad, store locations for a locator. This is a data reference, not a campaign expansion directive; campaign structure and budget allocation are handled by create_media_buy packages. Each catalog can be inline (with items), a reference to a synced catalog (by catalog_id), or a URL to an external feed.", - "items": { - "$ref": "/schemas/latest/core/catalog.json" - }, - "minItems": 1, - "type": "array" - }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "ext.json" }, "format_id": { - "$ref": "/schemas/latest/core/format-id.json", + "$ref": "format-id.json", "description": "Format identifier this manifest is for. Can be a template format (id only) or a deterministic format (id + dimensions/duration). For dimension-specific creatives, include width/height/unit in the format_id to create a unique identifier (e.g., {id: 'display_static', width: 300, height: 250, unit: 'px'})." + }, + "provenance": { + "$ref": "provenance.json", + "description": "Provenance metadata for this creative manifest. Serves as the default provenance for all assets in this manifest. An asset with its own provenance replaces this object entirely (no field-level merging)." } }, "required": [ diff --git a/schemas/cache/core/creative-policy.json b/schemas/cache/core/creative-policy.json index bf17bb57..128ff3ef 100644 --- a/schemas/cache/core/creative-policy.json +++ b/schemas/cache/core/creative-policy.json @@ -11,6 +11,10 @@ "$ref": "../enums/landing-page-requirement.json", "description": "Landing page requirements" }, + "provenance_required": { + "description": "Whether creatives must include provenance metadata. When true, the seller requires buyers to attach provenance declarations to creative submissions. The seller may independently verify claims via get_creative_features.", + "type": "boolean" + }, "templates_available": { "description": "Whether creative templates are provided", "type": "boolean" diff --git a/schemas/cache/core/date-range.json b/schemas/cache/core/date-range.json new file mode 100644 index 00000000..2b9893f4 --- /dev/null +++ b/schemas/cache/core/date-range.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "description": "A date range with inclusive start and end dates (ISO 8601 calendar dates). Used for billing periods, flight dates, and other calendar-day boundaries.", + "properties": { + "end": { + "description": "End date (inclusive), ISO 8601", + "format": "date", + "type": "string" + }, + "start": { + "description": "Start date (inclusive), ISO 8601", + "format": "date", + "type": "string" + } + }, + "required": [ + "start", + "end" + ], + "title": "Date Range", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/core/datetime-range.json b/schemas/cache/core/datetime-range.json new file mode 100644 index 00000000..1d4d89f5 --- /dev/null +++ b/schemas/cache/core/datetime-range.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "description": "A datetime range with inclusive start and end timestamps (ISO 8601 with timezone). Used for measurement windows, reporting periods, and other sub-day precision boundaries.", + "properties": { + "end": { + "description": "End timestamp (inclusive), ISO 8601", + "format": "date-time", + "type": "string" + }, + "start": { + "description": "Start timestamp (inclusive), ISO 8601", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "start", + "end" + ], + "title": "Datetime Range", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/core/delivery-metrics.json b/schemas/cache/core/delivery-metrics.json index e213dd10..39304754 100644 --- a/schemas/cache/core/delivery-metrics.json +++ b/schemas/cache/core/delivery-metrics.json @@ -73,7 +73,7 @@ "type": "number" }, "completed_views": { - "description": "100% completions (for CPCV)", + "description": "Video/audio completions. When the package has a completed_views optimization goal with view_duration_seconds, completions are counted at that threshold rather than 100% completion.", "minimum": 0, "type": "number" }, @@ -183,11 +183,21 @@ "type": "object" }, "engagement_rate": { - "description": "Platform-specific engagement rate (0.0 to 1.0). Definition varies by platform (e.g., likes+comments+shares/impressions on social, interactions/impressions on rich media).", + "description": "Platform-specific engagement rate (0.0 to 1.0). Typically engagements/impressions, but definition varies by platform.", "maximum": 1, "minimum": 0, "type": "number" }, + "engagements": { + "description": "Total engagements \u2014 direct interactions with the ad beyond viewing. Includes social reactions/comments/shares, story/unit opens, interactive overlay taps on CTV, companion banner interactions on audio. Platform-specific; corresponds to the 'engagements' optimization metric.", + "minimum": 0, + "type": "number" + }, + "follows": { + "description": "New followers, page likes, artist/podcast/channel subscribes attributed to this delivery.", + "minimum": 0, + "type": "number" + }, "frequency": { "description": "Average frequency per individual (typically measured over campaign duration, but can vary by measurement provider)", "minimum": 0, @@ -214,6 +224,11 @@ "minimum": 0, "type": "number" }, + "profile_visits": { + "description": "Visits to the brand's in-platform page (profile, artist page, channel, or storefront) attributed to this delivery. Does not include external website clicks.", + "minimum": 0, + "type": "number" + }, "quartile_data": { "description": "Video quartile completion data", "properties": { @@ -250,6 +265,11 @@ "minimum": 0, "type": "number" }, + "saves": { + "description": "Saves, bookmarks, playlist adds, pins attributed to this delivery.", + "minimum": 0, + "type": "number" + }, "spend": { "description": "Amount spent", "minimum": 0, diff --git a/schemas/cache/core/destination-item.json b/schemas/cache/core/destination-item.json index a66137f7..5ef65a9c 100644 --- a/schemas/cache/core/destination-item.json +++ b/schemas/cache/core/destination-item.json @@ -57,6 +57,14 @@ } ], "properties": { + "assets": { + "description": "Typed creative asset pools for this destination. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (destination hero), 'images_vertical' (9:16 for Snap, Stories), 'images_square' (1:1). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + "items": { + "$ref": "offering-asset-group.json" + }, + "minItems": 1, + "type": "array" + }, "city": { "description": "City name, if applicable.", "type": "string" diff --git a/schemas/cache/core/duration.json b/schemas/cache/core/duration.json new file mode 100644 index 00000000..dac28d1e --- /dev/null +++ b/schemas/cache/core/duration.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "description": "A time duration expressed as an interval and unit. Used for frequency cap windows, attribution windows, reach optimization windows, and other time-based settings. When unit is 'campaign', interval must be 1 \u2014 the window spans the full campaign flight.", + "properties": { + "interval": { + "description": "Number of time units. Must be 1 when unit is 'campaign'.", + "minimum": 1, + "type": "integer" + }, + "unit": { + "description": "Time unit. 'campaign' spans the full campaign flight.", + "enum": [ + "minutes", + "hours", + "days", + "campaign" + ], + "type": "string" + } + }, + "required": [ + "interval", + "unit" + ], + "title": "Duration", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/core/education-item.json b/schemas/cache/core/education-item.json index b6a65164..ea8d3cd8 100644 --- a/schemas/cache/core/education-item.json +++ b/schemas/cache/core/education-item.json @@ -55,6 +55,14 @@ } ], "properties": { + "assets": { + "description": "Typed creative asset pools for this program. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (campus/program hero), 'images_vertical' (9:16 for Stories), 'logo' (institution logo). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + "items": { + "$ref": "offering-asset-group.json" + }, + "minItems": 1, + "type": "array" + }, "degree_type": { "description": "Type of credential awarded.", "enum": [ diff --git a/schemas/cache/core/error.json b/schemas/cache/core/error.json index 31adf984..dddbd208 100644 --- a/schemas/cache/core/error.json +++ b/schemas/cache/core/error.json @@ -4,7 +4,7 @@ "description": "Standard error structure for task-specific errors and warnings", "properties": { "code": { - "description": "Error code for programmatic handling", + "description": "Error code for programmatic handling. Standard codes are defined in error-code.json and enable autonomous agent recovery. Sellers MAY use codes not in the standard vocabulary for platform-specific errors; agents MUST handle unknown codes gracefully by falling back to the recovery classification.", "type": "string" }, "details": { @@ -20,6 +20,15 @@ "description": "Human-readable error message", "type": "string" }, + "recovery": { + "description": "Agent recovery classification. transient: retry after delay (rate limit, service unavailable, timeout). correctable: fix the request and resend (invalid field, budget too low, creative rejected). terminal: requires human action (account suspended, payment required, account not found).", + "enum": [ + "transient", + "correctable", + "terminal" + ], + "type": "string" + }, "retry_after": { "description": "Seconds to wait before retrying the operation", "minimum": 0, diff --git a/schemas/cache/core/flight-item.json b/schemas/cache/core/flight-item.json index 8436f0ac..f3c7f8c1 100644 --- a/schemas/cache/core/flight-item.json +++ b/schemas/cache/core/flight-item.json @@ -63,6 +63,14 @@ "format": "date-time", "type": "string" }, + "assets": { + "description": "Typed creative asset pools for this flight. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (destination hero), 'images_vertical' (9:16 for Stories), 'images_square' (1:1). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + "items": { + "$ref": "offering-asset-group.json" + }, + "minItems": 1, + "type": "array" + }, "departure_time": { "description": "Departure date and time (ISO 8601).", "format": "date-time", diff --git a/schemas/cache/core/forecast-point.json b/schemas/cache/core/forecast-point.json index 6907becd..227836fd 100644 --- a/schemas/cache/core/forecast-point.json +++ b/schemas/cache/core/forecast-point.json @@ -12,7 +12,48 @@ "additionalProperties": { "$ref": "forecast-range.json" }, - "description": "Forecasted metric values at this budget level. Keys are either forecastable-metric values for delivery/engagement (impressions, reach, spend, etc.) or event-type values for outcomes (purchase, lead, app_install, etc.). Values are ForecastRange objects (low/mid/high). Use { \"mid\": value } for point estimates. Include spend when the platform predicts it will differ from budget.", + "description": "Forecasted metric values at this budget level. Keys are forecastable-metric enum values for delivery/engagement or event-type enum values for outcomes. Values are ForecastRange objects (low/mid/high). Use { \"mid\": value } for point estimates. Include spend when the platform predicts it will differ from budget. Additional keys beyond the documented properties are allowed for event-type values (purchase, lead, app_install, etc.).", + "properties": { + "audience_size": { + "$ref": "forecast-range.json" + }, + "clicks": { + "$ref": "forecast-range.json" + }, + "completed_views": { + "$ref": "forecast-range.json" + }, + "engagements": { + "$ref": "forecast-range.json" + }, + "follows": { + "$ref": "forecast-range.json" + }, + "frequency": { + "$ref": "forecast-range.json" + }, + "grps": { + "$ref": "forecast-range.json" + }, + "impressions": { + "$ref": "forecast-range.json" + }, + "profile_visits": { + "$ref": "forecast-range.json" + }, + "reach": { + "$ref": "forecast-range.json" + }, + "saves": { + "$ref": "forecast-range.json" + }, + "spend": { + "$ref": "forecast-range.json" + }, + "views": { + "$ref": "forecast-range.json" + } + }, "type": "object" } }, diff --git a/schemas/cache/core/format.json b/schemas/cache/core/format.json index 49aa622e..1d6df281 100644 --- a/schemas/cache/core/format.json +++ b/schemas/cache/core/format.json @@ -7,9 +7,16 @@ "type": "string" }, "asset_role": { - "description": "Optional descriptive label for this asset's purpose. Not used for referencing assets in manifests\u2014use asset_id instead. This field is for human-readable documentation and UI display only.", + "description": "Descriptive label for this asset's purpose. For documentation and UI display only \u2014 manifests key assets by asset_id, not asset_role.", "type": "string" }, + "overlays": { + "description": "Publisher-controlled elements rendered on top of buyer content at this asset's position (e.g., carousel navigation arrows, slide indicators). Creative agents should avoid placing critical content within overlay bounds.", + "items": { + "$ref": "overlay.json" + }, + "type": "array" + }, "required": { "default": false, "description": "Whether this asset is required within each repetition of the group", @@ -30,7 +37,7 @@ "type": "string" }, "asset_role": { - "description": "Optional descriptive label for this asset's purpose (e.g., 'hero_image', 'logo', 'third_party_tracking'). Not used for referencing assets in manifests\u2014use asset_id instead. This field is for human-readable documentation and UI display only.", + "description": "Descriptive label for this asset's purpose (e.g., 'hero_image', 'logo', 'third_party_tracking'). For documentation and UI display only \u2014 manifests key assets by asset_id, not asset_role.", "type": "string" }, "item_type": { @@ -38,6 +45,13 @@ "description": "Discriminator indicating this is an individual asset", "type": "string" }, + "overlays": { + "description": "Publisher-controlled elements rendered on top of buyer content at this asset's position (e.g., video player controls, publisher logos). Creative agents should avoid placing critical content (CTAs, logos, key copy) within overlay bounds.", + "items": { + "$ref": "overlay.json" + }, + "type": "array" + }, "required": { "description": "Whether this asset is required (true) or optional (false). Required assets must be provided for a valid creative. Optional assets enhance the creative but are not mandatory.", "type": "boolean" @@ -313,6 +327,41 @@ } } }, + { + "allOf": [ + { + "$ref": "#/$defs/baseIndividualAsset" + } + ], + "description": "Brief asset", + "properties": { + "asset_type": { + "const": "brief" + }, + "item_type": { + "const": "individual" + } + } + }, + { + "allOf": [ + { + "$ref": "#/$defs/baseIndividualAsset" + } + ], + "description": "Catalog asset", + "properties": { + "asset_type": { + "const": "catalog" + }, + "item_type": { + "const": "individual" + }, + "requirements": { + "$ref": "requirements/catalog-requirements.json" + } + } + }, { "description": "Repeatable asset group (for carousels, slideshows, playlists, etc.)", "properties": { @@ -563,14 +612,6 @@ }, "type": "array" }, - "catalog_requirements": { - "description": "Catalog feeds this format requires for rendering. Formats that display product listings, store locators, inventory availability, or promotional pricing declare what catalog types must be synced to the account. Buyers ensure the required catalogs are synced via sync_catalogs before submitting creatives in this format.", - "items": { - "$ref": "requirements/catalog-requirements.json" - }, - "minItems": 1, - "type": "array" - }, "delivery": { "additionalProperties": true, "description": "Delivery method specifications (e.g., hosted, VAST, third-party tags)", @@ -761,6 +802,15 @@ "type": "array", "uniqueItems": true }, + "supported_disclosure_positions": { + "description": "Disclosure positions this format can render. Buyers use this to determine whether a format can satisfy their compliance requirements before submitting a creative. When omitted, the format makes no disclosure rendering guarantees \u2014 creative agents SHOULD treat this as incompatible with briefs that require specific disclosure positions. Values correspond to positions on creative-brief.json required_disclosures.", + "items": { + "$ref": "../enums/disclosure-position.json" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + }, "supported_macros": { "description": "List of universal macros supported by this format (e.g., MEDIA_BUY_ID, CACHEBUSTER, DEVICE_ID). Used for validation and developer tooling. See docs/creative/universal-macros.mdx for full documentation.", "items": { diff --git a/schemas/cache/core/frequency-cap.json b/schemas/cache/core/frequency-cap.json index 7a658fd1..db86288c 100644 --- a/schemas/cache/core/frequency-cap.json +++ b/schemas/cache/core/frequency-cap.json @@ -1,17 +1,72 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, - "description": "Frequency capping settings for package-level application", + "anyOf": [ + { + "required": [ + "suppress" + ] + }, + { + "required": [ + "suppress_minutes" + ] + }, + { + "required": [ + "max_impressions" + ] + } + ], + "dependencies": { + "max_impressions": [ + "per", + "window" + ], + "per": [ + "max_impressions" + ], + "window": [ + "max_impressions" + ] + }, + "description": "Frequency capping settings for package-level application. Two types of frequency control can be used independently or together: suppress enforces a cooldown between consecutive exposures; max_impressions + per + window caps total exposures per entity in a time window. When both suppress and max_impressions are set, an impression is delivered only if both constraints permit it (AND semantics). At least one of suppress, suppress_minutes, or max_impressions must be set.", "properties": { + "max_impressions": { + "description": "Maximum number of impressions per entity per window. For duration windows, implementations typically use a rolling window; 'campaign' applies a fixed cap across the full flight.", + "minimum": 1, + "type": "integer" + }, + "per": { + "allOf": [ + { + "$ref": "../enums/reach-unit.json" + } + ], + "description": "Entity granularity for impression counting. Required when max_impressions is set." + }, + "suppress": { + "allOf": [ + { + "$ref": "duration.json" + } + ], + "description": "Cooldown period between consecutive exposures to the same entity. Prevents back-to-back ad delivery (e.g. {\"interval\": 60, \"unit\": \"minutes\"} for a 1-hour cooldown). Preferred over suppress_minutes." + }, "suppress_minutes": { - "description": "Minutes to suppress after impression", + "description": "Deprecated \u2014 use suppress instead. Cooldown period in minutes between consecutive exposures to the same entity (e.g. 60 for a 1-hour cooldown).", "minimum": 0, "type": "number" + }, + "window": { + "allOf": [ + { + "$ref": "duration.json" + } + ], + "description": "Time window for the max_impressions cap (e.g. {\"interval\": 7, \"unit\": \"days\"} or {\"interval\": 1, \"unit\": \"campaign\"} for the full flight). Required when max_impressions is set." } }, - "required": [ - "suppress_minutes" - ], "title": "Frequency Cap", "type": "object" } \ No newline at end of file diff --git a/schemas/cache/core/geo-breakdown-support.json b/schemas/cache/core/geo-breakdown-support.json new file mode 100644 index 00000000..378fd5a8 --- /dev/null +++ b/schemas/cache/core/geo-breakdown-support.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "description": "Declares which geographic levels and classification systems are available for by_geo reporting breakdowns in product-level reporting_capabilities.", + "properties": { + "country": { + "description": "Supports country-level geo breakdown (ISO 3166-1 alpha-2)", + "type": "boolean" + }, + "metro": { + "additionalProperties": { + "type": "boolean" + }, + "description": "Metro area breakdown support. Keys are metro-system enum values; true means supported.", + "propertyNames": { + "$ref": "../enums/metro-system.json" + }, + "type": "object" + }, + "postal_area": { + "additionalProperties": { + "type": "boolean" + }, + "description": "Postal area breakdown support. Keys are postal-system enum values; true means supported.", + "propertyNames": { + "$ref": "../enums/postal-system.json" + }, + "type": "object" + }, + "region": { + "description": "Supports region/state-level geo breakdown (ISO 3166-2)", + "type": "boolean" + } + }, + "title": "Geographic Breakdown Support", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/core/hotel-item.json b/schemas/cache/core/hotel-item.json index f48b60fb..72f23409 100644 --- a/schemas/cache/core/hotel-item.json +++ b/schemas/cache/core/hotel-item.json @@ -104,6 +104,14 @@ "minItems": 1, "type": "array" }, + "assets": { + "description": "Typed creative asset pools for this hotel. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (16:9 hero images), 'images_vertical' (9:16 for Snap, Stories), 'images_square' (1:1), 'logo'. Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + "items": { + "$ref": "offering-asset-group.json" + }, + "minItems": 1, + "type": "array" + }, "check_in_time": { "description": "Standard check-in time in HH:MM format (e.g., '15:00').", "pattern": "^[0-2][0-9]:[0-5][0-9]$", @@ -183,6 +191,16 @@ "description": "Property landing page or booking URL.", "format": "uri", "type": "string" + }, + "valid_from": { + "description": "Date from which this item is available or this rate applies (ISO 8601, e.g., '2025-03-01'). Used for seasonal availability windows in feed imports.", + "format": "date", + "type": "string" + }, + "valid_to": { + "description": "Date until which this item is available or this rate applies (ISO 8601, e.g., '2025-09-30'). Used for seasonal availability windows in feed imports.", + "format": "date", + "type": "string" } }, "required": [ diff --git a/schemas/cache/core/job-item.json b/schemas/cache/core/job-item.json index c0dc1626..db98a4e7 100644 --- a/schemas/cache/core/job-item.json +++ b/schemas/cache/core/job-item.json @@ -63,6 +63,14 @@ "format": "uri", "type": "string" }, + "assets": { + "description": "Typed creative asset pools for this job. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (company/role hero), 'images_vertical' (9:16 for Stories), 'logo' (company logo). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + "items": { + "$ref": "offering-asset-group.json" + }, + "minItems": 1, + "type": "array" + }, "company_name": { "description": "Hiring company or organization name.", "type": "string" diff --git a/schemas/cache/core/mcp-webhook-payload.json b/schemas/cache/core/mcp-webhook-payload.json index b0c9abce..91d112ab 100644 --- a/schemas/cache/core/mcp-webhook-payload.json +++ b/schemas/cache/core/mcp-webhook-payload.json @@ -115,7 +115,7 @@ "type": "string" }, "operation_id": { - "description": "Publisher-defined operation identifier correlating a sequence of task updates across webhooks.", + "description": "Client-generated identifier that was embedded in the webhook URL by the buyer. Publishers echo this back in webhook payloads so clients can correlate notifications without parsing URL paths. Typically generated as a unique ID per task invocation.", "type": "string" }, "result": { diff --git a/schemas/cache/core/media-buy.json b/schemas/cache/core/media-buy.json index d3c8ed58..9a4d5816 100644 --- a/schemas/cache/core/media-buy.json +++ b/schemas/cache/core/media-buy.json @@ -1,11 +1,10 @@ { - "$id": "/schemas/latest/core/media-buy.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Represents a purchased advertising campaign", "properties": { "account": { - "$ref": "/schemas/latest/core/account.json", + "$ref": "account.json", "description": "Account billed for this media buy" }, "buyer_campaign_ref": { @@ -27,7 +26,7 @@ "type": "string" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "ext.json" }, "media_buy_id": { "description": "Publisher's unique identifier for the media buy", @@ -36,12 +35,16 @@ "packages": { "description": "Array of packages within this media buy", "items": { - "$ref": "/schemas/latest/core/package.json" + "$ref": "package.json" }, "type": "array" }, + "rejection_reason": { + "description": "Reason provided by the seller when status is 'rejected'. Present only when status is 'rejected'.", + "type": "string" + }, "status": { - "$ref": "/schemas/latest/enums/media-buy-status.json" + "$ref": "../enums/media-buy-status.json" }, "total_budget": { "description": "Total budget amount", diff --git a/schemas/cache/core/optimization-goal.json b/schemas/cache/core/optimization-goal.json index a635782d..a1cce0d6 100644 --- a/schemas/cache/core/optimization-goal.json +++ b/schemas/cache/core/optimization-goal.json @@ -1,49 +1,284 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": true, - "description": "Conversion optimization goal for a package. Tells the seller which event source and event type to optimize delivery against. Provide at most one of target_roas or target_cpa. If neither is provided, the seller optimizes for maximum conversions within budget.", - "properties": { - "attribution_window": { + "description": "A single optimization target for a package. Packages accept an array of optimization_goals. When multiple goals are present, priority determines which the seller focuses on \u2014 1 is highest priority (primary goal); higher numbers are secondary. Duplicate priority values result in undefined seller behavior.", + "oneOf": [ + { "additionalProperties": true, - "description": "Attribution window for this optimization goal. Values must match an option declared in the seller's conversion_tracking.attribution_windows capability. When omitted, the seller uses their default window.", + "description": "Optimize for a seller-tracked delivery metric. No event source required \u2014 the seller tracks these natively.", "properties": { - "click_through": { - "description": "Click-through attribution window (e.g. '7d', '28d', '30d')", + "kind": { + "const": "metric", "type": "string" }, - "view_through": { - "description": "View-through attribution window (e.g. '1d', '7d')", + "metric": { + "description": "Seller-native metric to optimize for. Delivery metrics: clicks (link clicks, swipe-throughs, CTA taps that navigate away), views (viewable impressions), completed_views (video/audio completions \u2014 see view_duration_seconds), reach (unique audience reach \u2014 see reach_unit and target_frequency). Duration/score metrics: viewed_seconds (time in view per impression), attention_seconds (attention time per impression), attention_score (vendor-specific attention score). Audience action metrics: engagements (any direct interaction with the ad unit beyond viewing \u2014 social reactions/comments/shares, story/unit opens, interactive overlay taps, companion banner interactions on audio and CTV), follows (new followers, page likes, artist/podcast/channel subscribes), saves (saves, bookmarks, playlist adds, pins \u2014 signals of intent to return), profile_visits (visits to the brand's in-platform page \u2014 profile, artist page, channel, or storefront. Does not include external website clicks, which are covered by 'clicks').", + "enum": [ + "clicks", + "views", + "completed_views", + "viewed_seconds", + "attention_seconds", + "attention_score", + "engagements", + "follows", + "saves", + "profile_visits", + "reach" + ], "type": "string" + }, + "priority": { + "description": "Relative priority among all optimization goals on this package. 1 = highest priority (primary goal); higher numbers are lower priority (secondary signals). When omitted, sellers may use array position as priority.", + "minimum": 1, + "type": "integer" + }, + "reach_unit": { + "allOf": [ + { + "$ref": "../enums/reach-unit.json" + } + ], + "description": "Unit for reach measurement. Required when metric is 'reach'. Must be a value declared in the product's metric_optimization.supported_reach_units." + }, + "target": { + "description": "Target for this metric. When omitted, the seller optimizes for maximum metric volume within budget.", + "oneOf": [ + { + "additionalProperties": true, + "description": "Target cost per unit of the metric (e.g., cost per click, cost per completed view).", + "properties": { + "kind": { + "const": "cost_per", + "type": "string" + }, + "value": { + "description": "Target cost per metric unit in the buy currency", + "exclusiveMinimum": 0, + "type": "number" + } + }, + "required": [ + "kind", + "value" + ], + "type": "object" + }, + { + "additionalProperties": true, + "description": "Minimum per-impression rate for this metric. The metric defines the units: proportions for count metrics (e.g., 0.001 for 0.1% CTR, 0.70 for 70% viewability), seconds for duration metrics (e.g., 3.0 for 3s in view), or score for score metrics.", + "properties": { + "kind": { + "const": "threshold_rate", + "type": "string" + }, + "value": { + "description": "Minimum per-impression value. Units depend on the metric: proportion (clicks, views, completed_views), seconds (viewed_seconds, attention_seconds), or score (attention_score).", + "exclusiveMinimum": 0, + "type": "number" + } + }, + "required": [ + "kind", + "value" + ], + "type": "object" + } + ] + }, + "target_frequency": { + "additionalProperties": true, + "anyOf": [ + { + "required": [ + "min" + ] + }, + { + "required": [ + "max" + ] + } + ], + "description": "Target frequency band for reach optimization. Only applicable when metric is 'reach'. Frames frequency as an optimization signal: the seller should treat impressions toward entities already within the [min, max] band as lower-value, and impressions toward unreached entities as higher-value. This shifts budget toward fresh reach rather than re-reaching known users. When omitted, the seller maximizes unique reach without a frequency constraint. A hard cap can still be layered via targeting_overlay.frequency_cap if a ceiling is needed.", + "properties": { + "max": { + "description": "Frequency at which an entity is considered saturated within the window. Impressions toward entities at or above this threshold are treated as lower-value. When both min and max are present, max must be greater than or equal to min. When omitted, the seller determines the saturation point.", + "minimum": 1, + "type": "integer" + }, + "min": { + "description": "Minimum frequency for an entity to be considered meaningfully reached within the window. Impressions that would bring an entity below this threshold are treated as high-value (growing reach). When omitted, the seller uses their platform default (typically 1).", + "minimum": 1, + "type": "integer" + }, + "window": { + "allOf": [ + { + "$ref": "duration.json" + } + ], + "description": "Time window over which frequency is measured (e.g. {\"interval\": 7, \"unit\": \"days\"} or {\"interval\": 1, \"unit\": \"campaign\"} for the full flight). Weekly windows are typical for brand campaigns; daily windows suit high-cadence direct response." + } + }, + "required": [ + "window" + ], + "type": "object" + }, + "view_duration_seconds": { + "description": "Minimum video view duration in seconds that qualifies as a completed_view for this goal. Only applicable when metric is 'completed_views'. When omitted, the seller uses their platform default (typically 2\u201315 seconds). Common values: 2 (Snap/LinkedIn default), 6 (TikTok), 15 (Snap 15-second views, Meta ThruPlay). Sellers declare which durations they support in metric_optimization.supported_view_durations. Sellers must reject goals with unsupported values \u2014 silent rounding would create measurement discrepancies.", + "exclusiveMinimum": 0, + "type": "number" } }, "required": [ - "click_through" + "kind", + "metric" ], "type": "object" }, - "event_source_id": { - "description": "Event source to optimize against (must be configured on this account via sync_event_sources)", - "type": "string" - }, - "event_type": { - "$ref": "../enums/event-type.json", - "description": "Event type to optimize for (e.g. purchase, lead)" - }, - "target_cpa": { - "description": "Target cost per acquisition in the buy currency. Mutually exclusive with target_roas.", - "exclusiveMinimum": 0, - "type": "number" - }, - "target_roas": { - "description": "Target return on ad spend (e.g. 4.0 = $4 conversion value per $1 spent). Mutually exclusive with target_cpa.", - "exclusiveMinimum": 0, - "type": "number" + { + "additionalProperties": true, + "description": "Optimize for advertiser-tracked conversion events. Requires event sources registered via sync_event_sources.", + "properties": { + "attribution_window": { + "additionalProperties": true, + "description": "Attribution window for this optimization goal. Values must match an option declared in the seller's conversion_tracking.attribution_windows capability. Sellers must reject windows not in their declared capabilities. When omitted, the seller uses their default window.", + "properties": { + "post_click": { + "allOf": [ + { + "$ref": "duration.json" + } + ], + "description": "Post-click attribution window. Conversions within this duration after a click are attributed to the ad (e.g. {\"interval\": 7, \"unit\": \"days\"})." + }, + "post_view": { + "allOf": [ + { + "$ref": "duration.json" + } + ], + "description": "Post-view attribution window. Conversions within this duration after an ad impression (without click) are attributed to the ad (e.g. {\"interval\": 1, \"unit\": \"days\"})." + } + }, + "required": [ + "post_click" + ], + "type": "object" + }, + "event_sources": { + "description": "Event source and type pairs that feed this goal. Each entry identifies a source and event type to include. When the seller supports multi_source_event_dedup (declared in get_adcp_capabilities), they deduplicate by event_id across all entries \u2014 the same business event from multiple sources counts once, using value_field and value_factor from the first matching entry. When multi_source_event_dedup is false or absent, buyers should use a single entry per goal; the seller will use only the first entry. All event sources must be configured via sync_event_sources.", + "items": { + "additionalProperties": true, + "properties": { + "custom_event_name": { + "description": "Required when event_type is 'custom'. Platform-specific name for the custom event.", + "type": "string" + }, + "event_source_id": { + "description": "Event source to include (must be configured on this account via sync_event_sources)", + "minLength": 1, + "type": "string" + }, + "event_type": { + "$ref": "../enums/event-type.json", + "description": "Event type to include from this source (e.g., purchase, lead, app_install, refund)" + }, + "value_factor": { + "default": 1, + "description": "Multiplier the seller must apply to value_field before aggregation. Use -1 for refund events (negate the value), 0.01 for values in cents, -0.01 for refunds in cents. A value of 0 zeroes out this source's value contribution (the source still counts for event dedup). Defaults to 1. This is not passed as a parameter to underlying platform APIs \u2014 the seller applies it when computing aggregated value metrics.", + "type": "number" + }, + "value_field": { + "description": "Which field in the event's custom_data carries the monetary value. The seller must use this field for value extraction and aggregation when computing ROAS and conversion value metrics. Required on at least one entry when target.kind is 'per_ad_spend' or 'maximize_value'. Common values: 'value', 'order_total', 'profit_margin'. This is not passed as a parameter to underlying platform APIs \u2014 the seller maps it to their platform's value ingestion mechanism.", + "type": "string" + } + }, + "required": [ + "event_source_id", + "event_type" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "kind": { + "const": "event", + "type": "string" + }, + "priority": { + "description": "Relative priority among all optimization goals on this package. 1 = highest priority (primary goal); higher numbers are lower priority (secondary signals). When omitted, sellers may use array position as priority.", + "minimum": 1, + "type": "integer" + }, + "target": { + "description": "Target cost or return for this event goal. When omitted, the seller optimizes for maximum conversions within budget.", + "oneOf": [ + { + "additionalProperties": true, + "description": "Target cost per conversion event (after deduplication across event_sources).", + "properties": { + "kind": { + "const": "cost_per", + "type": "string" + }, + "value": { + "description": "Target cost per event in the buy currency", + "exclusiveMinimum": 0, + "type": "number" + } + }, + "required": [ + "kind", + "value" + ], + "type": "object" + }, + { + "additionalProperties": true, + "description": "Target return per unit of ad spend, calculated as sum(value_field * value_factor) / spend across all deduplicated events. Requires value_field on at least one event_sources entry.", + "properties": { + "kind": { + "const": "per_ad_spend", + "type": "string" + }, + "value": { + "description": "Target return ratio (e.g., 4.0 means $4 of value per $1 spent)", + "exclusiveMinimum": 0, + "type": "number" + } + }, + "required": [ + "kind", + "value" + ], + "type": "object" + }, + { + "additionalProperties": true, + "description": "Maximize total conversion value within budget, without a specific return ratio target. Steers spend toward higher-value conversions rather than maximizing conversion count. Requires value_field on at least one event_sources entry.", + "properties": { + "kind": { + "const": "maximize_value", + "type": "string" + } + }, + "required": [ + "kind" + ], + "type": "object" + } + ] + } + }, + "required": [ + "kind", + "event_sources" + ], + "type": "object" } - }, - "required": [ - "event_source_id", - "event_type" ], - "title": "Optimization Goal", - "type": "object" + "title": "Optimization Goal" } \ No newline at end of file diff --git a/schemas/cache/core/measurement.json b/schemas/cache/core/outcome-measurement.json similarity index 64% rename from schemas/cache/core/measurement.json rename to schemas/cache/core/outcome-measurement.json index b0ba1c51..b653407f 100644 --- a/schemas/cache/core/measurement.json +++ b/schemas/cache/core/outcome-measurement.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, - "description": "Measurement capabilities included with a product", + "description": "Business outcome measurement capabilities included with a product (e.g., incremental sales lift, brand lift, foot traffic). Distinct from delivery_measurement, which declares who counts ad impressions.", "properties": { "attribution": { "description": "Attribution methodology", @@ -29,12 +29,12 @@ "type": "string" }, "window": { - "description": "Attribution window", - "examples": [ - "30_days", - "7_days" + "allOf": [ + { + "$ref": "duration.json" + } ], - "type": "string" + "description": "Attribution window as a structured duration (e.g., {\"interval\": 30, \"unit\": \"days\"})." } }, "required": [ @@ -42,6 +42,6 @@ "attribution", "reporting" ], - "title": "Measurement", + "title": "Outcome Measurement", "type": "object" } \ No newline at end of file diff --git a/schemas/cache/core/overlay.json b/schemas/cache/core/overlay.json new file mode 100644 index 00000000..27f06fb4 --- /dev/null +++ b/schemas/cache/core/overlay.json @@ -0,0 +1,84 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "description": "A publisher-controlled element that renders on top of buyer creative content within the ad placement. Creative agents should avoid placing critical content (CTAs, logos, key copy) within overlay bounds.", + "properties": { + "bounds": { + "additionalProperties": false, + "description": "Position and size of the overlay relative to the asset's own top-left corner. See 'unit' for coordinate interpretation.", + "properties": { + "height": { + "description": "Height of the overlay", + "minimum": 0, + "type": "number" + }, + "unit": { + "description": "'px' = absolute pixels from asset top-left. 'fraction' = proportional to asset dimensions (x/y: 0.0 = asset edge, 1.0 = opposite edge; width/height: 0.12 = 12% of asset dimension).", + "enum": [ + "px", + "fraction" + ], + "type": "string" + }, + "width": { + "description": "Width of the overlay", + "minimum": 0, + "type": "number" + }, + "x": { + "description": "Horizontal offset from the asset's left edge", + "type": "number" + }, + "y": { + "description": "Vertical offset from the asset's top edge", + "type": "number" + } + }, + "required": [ + "x", + "y", + "width", + "height", + "unit" + ], + "type": "object" + }, + "description": { + "description": "Human-readable explanation of what this overlay is and how buyers should account for it", + "type": "string" + }, + "id": { + "description": "Identifier for this overlay (e.g., 'play_pause', 'volume', 'publisher_logo', 'carousel_prev', 'carousel_next')", + "type": "string" + }, + "visual": { + "additionalProperties": false, + "description": "Optional visual reference for this overlay element. Useful for creative agents compositing previews and for buyers understanding what will appear over their content. Must include at least one of: url, light, or dark.", + "minProperties": 1, + "properties": { + "dark": { + "description": "URL to the overlay graphic for use on dark backgrounds (SVG or PNG)", + "format": "uri", + "type": "string" + }, + "light": { + "description": "URL to the overlay graphic for use on light/bright backgrounds (SVG or PNG)", + "format": "uri", + "type": "string" + }, + "url": { + "description": "URL to a theme-neutral overlay graphic (SVG or PNG). Use when a single file works for all backgrounds, e.g. an SVG using CSS custom properties or currentColor.", + "format": "uri", + "type": "string" + } + }, + "type": "object" + } + }, + "required": [ + "id", + "bounds" + ], + "title": "Overlay", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/core/package.json b/schemas/cache/core/package.json index 24aab766..baea07ab 100644 --- a/schemas/cache/core/package.json +++ b/schemas/cache/core/package.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/core/package.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "A specific product within a media buy (line item)", @@ -18,20 +17,34 @@ "description": "Buyer's reference identifier for this package", "type": "string" }, + "catalogs": { + "description": "Catalogs this package promotes. Each catalog MUST have a distinct type (e.g., one product catalog, one store catalog). This constraint is enforced at the application level \u2014 sellers MUST reject requests containing multiple catalogs of the same type with a validation_error. Echoed from the create_media_buy request.", + "items": { + "$ref": "catalog.json" + }, + "type": "array" + }, "creative_assignments": { "description": "Creative assets assigned to this package", "items": { - "$ref": "/schemas/latest/core/creative-assignment.json" + "$ref": "creative-assignment.json" }, "type": "array" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "ext.json" + }, + "format_ids": { + "description": "Format IDs active for this package. Echoed from the create_media_buy request; omitted means all formats for the product are active.", + "items": { + "$ref": "format-id.json" + }, + "type": "array" }, "format_ids_to_provide": { "description": "Format IDs that creative assets will be provided for this package", "items": { - "$ref": "/schemas/latest/core/format-id.json" + "$ref": "format-id.json" }, "type": "array" }, @@ -40,11 +53,16 @@ "minimum": 0, "type": "number" }, - "optimization_goal": { - "$ref": "/schemas/latest/core/optimization-goal.json" + "optimization_goals": { + "description": "Optimization targets for this package. The seller optimizes delivery toward these goals in priority order. Common pattern: event goals (purchase, install) as primary targets at priority 1; metric goals (clicks, views) as secondary proxy signals at priority 2+.", + "items": { + "$ref": "optimization-goal.json" + }, + "minItems": 1, + "type": "array" }, "pacing": { - "$ref": "/schemas/latest/enums/pacing.json" + "$ref": "../enums/pacing.json" }, "package_id": { "description": "Publisher's unique identifier for the package", @@ -64,7 +82,7 @@ "type": "string" }, "targeting_overlay": { - "$ref": "/schemas/latest/core/targeting.json" + "$ref": "targeting.json" } }, "required": [ diff --git a/schemas/cache/core/pricing-option.json b/schemas/cache/core/pricing-option.json index bbb5ccf6..adc7f7c8 100644 --- a/schemas/cache/core/pricing-option.json +++ b/schemas/cache/core/pricing-option.json @@ -1,34 +1,33 @@ { - "$id": "/schemas/latest/core/pricing-option.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A pricing model option offered by a publisher for a product. Discriminated by pricing_model field. If fixed_price is present, it's fixed pricing. If absent, it's auction-based (floor_price and price_guidance optional). Bid-based auction models may also include max_bid as a boolean signal to interpret bid_price as a buyer ceiling instead of an exact honored price.", "oneOf": [ { - "$ref": "/schemas/latest/pricing-options/cpm-option.json" + "$ref": "../pricing-options/cpm-option.json" }, { - "$ref": "/schemas/latest/pricing-options/vcpm-option.json" + "$ref": "../pricing-options/vcpm-option.json" }, { - "$ref": "/schemas/latest/pricing-options/cpc-option.json" + "$ref": "../pricing-options/cpc-option.json" }, { - "$ref": "/schemas/latest/pricing-options/cpcv-option.json" + "$ref": "../pricing-options/cpcv-option.json" }, { - "$ref": "/schemas/latest/pricing-options/cpv-option.json" + "$ref": "../pricing-options/cpv-option.json" }, { - "$ref": "/schemas/latest/pricing-options/cpp-option.json" + "$ref": "../pricing-options/cpp-option.json" }, { - "$ref": "/schemas/latest/pricing-options/cpa-option.json" + "$ref": "../pricing-options/cpa-option.json" }, { - "$ref": "/schemas/latest/pricing-options/flat-rate-option.json" + "$ref": "../pricing-options/flat-rate-option.json" }, { - "$ref": "/schemas/latest/pricing-options/time-option.json" + "$ref": "../pricing-options/time-option.json" } ], "title": "Pricing Option" diff --git a/schemas/cache/core/product.json b/schemas/cache/core/product.json index 9ceb9615..7bddbc91 100644 --- a/schemas/cache/core/product.json +++ b/schemas/cache/core/product.json @@ -60,7 +60,7 @@ }, "conversion_tracking": { "additionalProperties": true, - "description": "Conversion tracking for this product. Presence indicates the product supports conversion-optimized delivery. Seller-level capabilities (supported event types, UID types, attribution windows) are declared in get_adcp_capabilities.", + "description": "Conversion event tracking for this product. Presence indicates the product supports optimization_goals with kind: 'event'. Seller-level capabilities (supported event types, UID types, attribution windows) are declared in get_adcp_capabilities.", "properties": { "action_sources": { "description": "Action sources relevant to this product (e.g. a retail media product might have 'in_store' and 'website', while a display product might only have 'website')", @@ -74,13 +74,13 @@ "description": "Whether the seller provides its own always-on measurement (e.g. Amazon sales attribution for Amazon advertisers). When true, sync_event_sources response will include seller-managed event sources with managed_by='seller'.", "type": "boolean" }, - "supported_optimization_strategies": { - "description": "Optimization strategies this product supports when an optimization_goal is set on a package", + "supported_targets": { + "description": "Target kinds available for event goals on this product. Values match target.kind on the optimization goal. cost_per: target cost per conversion event. per_ad_spend: target return on ad spend (requires value_field on event sources). maximize_value: maximize total conversion value without a specific ratio target (requires value_field). Only these target kinds are accepted \u2014 goals with unlisted target kinds will be rejected. A goal without a target implicitly maximizes conversion count within budget \u2014 no declaration needed for that mode. When omitted, buyers can still set target-less event goals.", "items": { "enum": [ - "maximize_conversions", - "target_cpa", - "target_roas" + "cost_per", + "per_ad_spend", + "maximize_value" ], "type": "string" }, @@ -147,13 +147,76 @@ "description": "Whether this is a custom product", "type": "boolean" }, - "measurement": { - "$ref": "measurement.json" + "max_optimization_goals": { + "description": "Maximum number of optimization_goals this product accepts on a package. When absent, no limit is declared. Most social platforms accept only 1 goal \u2014 buyers sending arrays longer than this value should expect the seller to use only the highest-priority (lowest priority number) goal.", + "minimum": 1, + "type": "integer" + }, + "metric_optimization": { + "additionalProperties": true, + "description": "Metric optimization capabilities for this product. Presence indicates the product supports optimization_goals with kind: 'metric'. No event source or conversion tracking setup required \u2014 the seller tracks these metrics natively.", + "properties": { + "supported_metrics": { + "description": "Metric kinds this product can optimize for. Buyers should only request metric goals for kinds listed here.", + "items": { + "enum": [ + "clicks", + "views", + "completed_views", + "viewed_seconds", + "attention_seconds", + "attention_score", + "engagements", + "follows", + "saves", + "profile_visits", + "reach" + ], + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "supported_reach_units": { + "description": "Reach units this product can optimize for. Required when supported_metrics includes 'reach'. Buyers must set reach_unit to a value in this list on reach optimization goals \u2014 sellers reject unsupported values.", + "items": { + "$ref": "../enums/reach-unit.json" + }, + "minItems": 1, + "type": "array" + }, + "supported_targets": { + "description": "Target kinds available for metric goals on this product. Values match target.kind on the optimization goal. Only these target kinds are accepted \u2014 goals with unlisted target kinds will be rejected. When omitted, buyers can set target-less metric goals (maximize volume within budget) but cannot set specific targets.", + "items": { + "enum": [ + "cost_per", + "threshold_rate" + ], + "type": "string" + }, + "type": "array" + }, + "supported_view_durations": { + "description": "Video view duration thresholds (in seconds) this product supports for completed_views goals. Only relevant when supported_metrics includes 'completed_views'. When absent, the seller uses their platform default. Buyers must set view_duration_seconds to a value in this list \u2014 sellers reject unsupported values.", + "items": { + "exclusiveMinimum": 0, + "type": "number" + }, + "type": "array" + } + }, + "required": [ + "supported_metrics" + ], + "type": "object" }, "name": { "description": "Human-readable product name", "type": "string" }, + "outcome_measurement": { + "$ref": "outcome-measurement.json" + }, "placements": { "description": "Optional array of specific placements within this product. When provided, buyers can target specific placements when assigning creatives.", "items": { diff --git a/schemas/cache/core/provenance.json b/schemas/cache/core/provenance.json new file mode 100644 index 00000000..4536ec4c --- /dev/null +++ b/schemas/cache/core/provenance.json @@ -0,0 +1,199 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": true, + "description": "Declares how content was produced, whether AI was involved, and what disclosure obligations apply. Attaches to creative manifests, individual assets, or content-standards artifacts. When present at multiple levels, the most specific provenance object replaces the inherited one entirely (no field-level merging). Provenance is a claim by the declaring party \u2014 receiving parties should verify claims independently via their own detection tools.", + "properties": { + "ai_tool": { + "additionalProperties": true, + "description": "AI system used to generate or modify this content. Aligns with IPTC 2025.1 AI metadata fields and C2PA claim_generator.", + "properties": { + "name": { + "description": "Name of the AI tool or model (e.g., 'DALL-E 3', 'Stable Diffusion XL', 'Gemini')", + "type": "string" + }, + "provider": { + "description": "Organization that provides the AI tool (e.g., 'OpenAI', 'Stability AI', 'Google')", + "type": "string" + }, + "version": { + "description": "Version identifier for the AI tool", + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "c2pa": { + "additionalProperties": true, + "description": "C2PA Content Credentials reference. Links to the cryptographic provenance manifest for this content. Because file-level C2PA bindings break during ad-tech transcoding, this URL reference preserves the chain of provenance through the supply chain.", + "properties": { + "manifest_url": { + "description": "URL to the C2PA manifest store for this content", + "format": "uri", + "type": "string" + } + }, + "required": [ + "manifest_url" + ], + "type": "object" + }, + "created_time": { + "description": "When this content was created or generated (ISO 8601)", + "format": "date-time", + "type": "string" + }, + "declared_by": { + "additionalProperties": true, + "description": "Party declaring this provenance. Identifies who attached the provenance claim, enabling receiving parties to assess trust.", + "properties": { + "agent_url": { + "description": "URL of the agent or service that declared this provenance", + "format": "uri", + "type": "string" + }, + "role": { + "description": "Role of the declaring party in the supply chain", + "enum": [ + "creator", + "advertiser", + "agency", + "platform", + "tool" + ], + "enumDescriptions": { + "advertiser": "The brand or advertiser that owns the content", + "agency": "Agency acting on behalf of the advertiser", + "creator": "The party that created or generated the content", + "platform": "Ad platform or publisher that processed the content", + "tool": "Automated tool or service that attached provenance metadata" + }, + "type": "string" + } + }, + "required": [ + "role" + ], + "type": "object" + }, + "digital_source_type": { + "$ref": "../enums/digital-source-type.json", + "description": "IPTC-aligned classification of AI involvement in producing this content" + }, + "disclosure": { + "additionalProperties": true, + "description": "Regulatory disclosure requirements for this content. Indicates whether AI disclosure is required and under which jurisdictions.", + "properties": { + "jurisdictions": { + "description": "Jurisdictions where disclosure obligations apply", + "items": { + "additionalProperties": true, + "properties": { + "country": { + "description": "ISO 3166-1 alpha-2 country code (e.g., 'US', 'DE', 'CN')", + "type": "string" + }, + "label_text": { + "description": "Required disclosure label text for this jurisdiction, in the local language", + "type": "string" + }, + "region": { + "description": "Sub-national region code (e.g., 'CA' for California, 'BY' for Bavaria)", + "type": "string" + }, + "regulation": { + "description": "Regulation identifier (e.g., 'eu_ai_act_article_50', 'ca_sb_942', 'cn_deep_synthesis')", + "type": "string" + } + }, + "required": [ + "country", + "regulation" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "required": { + "description": "Whether AI disclosure is required for this content based on applicable regulations", + "type": "boolean" + } + }, + "required": [ + "required" + ], + "type": "object" + }, + "ext": { + "$ref": "ext.json" + }, + "human_oversight": { + "description": "Level of human involvement in the AI-assisted creation process", + "enum": [ + "none", + "prompt_only", + "selected", + "edited", + "directed" + ], + "enumDescriptions": { + "directed": "Human directed the creative process with AI as an assistive tool", + "edited": "Human edited or refined AI-generated output", + "none": "Fully automated with no human involvement in generation", + "prompt_only": "Human provided the prompt or instructions but did not review outputs", + "selected": "Human selected from multiple AI-generated candidates" + }, + "type": "string" + }, + "verification": { + "description": "Third-party verification or detection results for this content. Multiple services may independently evaluate the same content. Provenance is a claim \u2014 verification results attached by the declaring party are supplementary. The enforcing party (e.g., seller/publisher) should run its own verification via get_creative_features or calibrate_content.", + "items": { + "additionalProperties": true, + "properties": { + "confidence": { + "description": "Confidence score of the verification result (0.0 to 1.0)", + "maximum": 1, + "minimum": 0, + "type": "number" + }, + "details_url": { + "description": "URL to the full verification report", + "format": "uri", + "type": "string" + }, + "result": { + "description": "Verification outcome", + "enum": [ + "authentic", + "ai_generated", + "ai_modified", + "inconclusive" + ], + "type": "string" + }, + "verified_by": { + "description": "Name of the verification service (e.g., 'DoubleVerify', 'Hive Moderation', 'Reality Defender')", + "type": "string" + }, + "verified_time": { + "description": "When the verification was performed (ISO 8601)", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "verified_by", + "result" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + } + }, + "title": "Provenance", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/core/real-estate-item.json b/schemas/cache/core/real-estate-item.json index dc75e57b..cf9e49c9 100644 --- a/schemas/cache/core/real-estate-item.json +++ b/schemas/cache/core/real-estate-item.json @@ -123,6 +123,14 @@ ], "type": "object" }, + "assets": { + "description": "Typed creative asset pools for this property listing. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (exterior/interior hero), 'images_vertical' (9:16 for Stories), 'images_square' (1:1). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + "items": { + "$ref": "offering-asset-group.json" + }, + "minItems": 1, + "type": "array" + }, "bathrooms": { "description": "Number of bathrooms (e.g., 2.5 for two full and one half bath).", "minimum": 0, diff --git a/schemas/cache/core/reporting-capabilities.json b/schemas/cache/core/reporting-capabilities.json index ad23b29f..ca370322 100644 --- a/schemas/cache/core/reporting-capabilities.json +++ b/schemas/cache/core/reporting-capabilities.json @@ -52,10 +52,34 @@ "minimum": 0, "type": "integer" }, + "supports_audience_breakdown": { + "description": "Whether this product supports audience segment breakdowns in delivery reporting (by_audience within by_package)", + "type": "boolean" + }, "supports_creative_breakdown": { "description": "Whether this product supports creative-level metric breakdowns in delivery reporting (by_creative within by_package)", "type": "boolean" }, + "supports_device_platform_breakdown": { + "description": "Whether this product supports device platform breakdowns in delivery reporting (by_device_platform within by_package)", + "type": "boolean" + }, + "supports_device_type_breakdown": { + "description": "Whether this product supports device type breakdowns in delivery reporting (by_device_type within by_package)", + "type": "boolean" + }, + "supports_geo_breakdown": { + "$ref": "geo-breakdown-support.json", + "description": "Geographic breakdown support for this product. Declares which geo levels and systems are available for by_geo reporting within by_package." + }, + "supports_keyword_breakdown": { + "description": "Whether this product supports keyword-level metric breakdowns in delivery reporting (by_keyword within by_package)", + "type": "boolean" + }, + "supports_placement_breakdown": { + "description": "Whether this product supports placement breakdowns in delivery reporting (by_placement within by_package)", + "type": "boolean" + }, "supports_webhooks": { "description": "Whether this product supports webhook-based reporting notifications", "type": "boolean" diff --git a/schemas/cache/core/requirements/catalog-field-binding.json b/schemas/cache/core/requirements/catalog-field-binding.json new file mode 100644 index 00000000..66338b3c --- /dev/null +++ b/schemas/cache/core/requirements/catalog-field-binding.json @@ -0,0 +1,171 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "AssetPoolBinding": { + "additionalProperties": true, + "description": "Maps an individual format asset to a typed asset pool on the catalog item (e.g., images_landscape, images_vertical, logo). The format slot receives the first item in the pool.", + "properties": { + "asset_group_id": { + "description": "The asset_group_id on the catalog item's assets array to pull from (e.g., 'images_landscape', 'images_vertical', 'logo').", + "type": "string" + }, + "asset_id": { + "description": "The asset_id from the format's assets array. Identifies which individual template slot this binding applies to.", + "type": "string" + }, + "ext": { + "$ref": "../../core/ext.json" + }, + "kind": { + "const": "asset_pool", + "type": "string" + } + }, + "required": [ + "kind", + "asset_id", + "asset_group_id" + ], + "type": "object" + }, + "ScalarBinding": { + "additionalProperties": true, + "description": "Maps an individual format asset to a catalog item field via dot-notation path.", + "properties": { + "asset_id": { + "description": "The asset_id from the format's assets array. Identifies which individual template slot this binding applies to.", + "type": "string" + }, + "catalog_field": { + "description": "Dot-notation path to the field on the catalog item (e.g., 'name', 'price.amount', 'location.city').", + "type": "string" + }, + "ext": { + "$ref": "../../core/ext.json" + }, + "kind": { + "const": "scalar", + "type": "string" + } + }, + "required": [ + "kind", + "asset_id", + "catalog_field" + ], + "type": "object" + } + }, + "description": "Maps a format template slot to a catalog item field or typed asset pool. The 'kind' field identifies the binding variant. All bindings are optional \u2014 agents can still infer mappings without them.", + "examples": [ + { + "data": { + "asset_id": "headline", + "catalog_field": "name", + "kind": "scalar" + }, + "description": "Scalar binding \u2014 hotel name to headline slot" + }, + { + "data": { + "asset_id": "price_badge", + "catalog_field": "price.amount", + "kind": "scalar" + }, + "description": "Scalar binding \u2014 nested field (nightly rate)" + }, + { + "data": { + "asset_group_id": "images_landscape", + "asset_id": "hero_image", + "kind": "asset_pool" + }, + "description": "Asset pool binding \u2014 hero image from landscape pool" + }, + { + "data": { + "asset_group_id": "images_vertical", + "asset_id": "snap_background", + "kind": "asset_pool" + }, + "description": "Asset pool binding \u2014 Snap vertical background from vertical pool" + }, + { + "data": { + "catalog_item": true, + "format_group_id": "slide", + "kind": "catalog_group", + "per_item_bindings": [ + { + "asset_id": "title", + "catalog_field": "name", + "kind": "scalar" + }, + { + "asset_id": "price", + "catalog_field": "price.amount", + "kind": "scalar" + }, + { + "asset_group_id": "images_landscape", + "asset_id": "image", + "kind": "asset_pool" + } + ] + }, + "description": "Catalog group binding \u2014 carousel where each slide is one hotel" + } + ], + "oneOf": [ + { + "$ref": "#/definitions/ScalarBinding" + }, + { + "$ref": "#/definitions/AssetPoolBinding" + }, + { + "additionalProperties": true, + "description": "A format repeatable_group where each repetition corresponds to one catalog item. Iterates the group over catalog items in catalog item order.", + "properties": { + "catalog_item": { + "const": true, + "description": "Each repetition of the format's repeatable_group maps to one item from the catalog.", + "type": "boolean" + }, + "ext": { + "$ref": "../../core/ext.json" + }, + "format_group_id": { + "description": "The asset_group_id of a repeatable_group in the format's assets array.", + "type": "string" + }, + "kind": { + "const": "catalog_group", + "type": "string" + }, + "per_item_bindings": { + "description": "Scalar and asset pool bindings that apply within each repetition of the group. Nested catalog_group bindings are not permitted.", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/ScalarBinding" + }, + { + "$ref": "#/definitions/AssetPoolBinding" + } + ] + }, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "kind", + "format_group_id", + "catalog_item" + ], + "type": "object" + } + ], + "title": "Catalog Field Binding" +} \ No newline at end of file diff --git a/schemas/cache/core/requirements/catalog-requirements.json b/schemas/cache/core/requirements/catalog-requirements.json index f943873b..bcf8a725 100644 --- a/schemas/cache/core/requirements/catalog-requirements.json +++ b/schemas/cache/core/requirements/catalog-requirements.json @@ -16,13 +16,27 @@ "type": "array", "uniqueItems": true }, + "field_bindings": { + "description": "Explicit mappings from format template slots to catalog item fields or typed asset pools. Optional \u2014 creative agents can infer mappings without them, but bindings make the relationship self-describing and enable validation. Covers scalar fields (asset_id \u2192 catalog_field), asset pools (asset_id \u2192 asset_group_id on the catalog item), and repeatable groups that iterate over catalog items.", + "items": { + "$ref": "catalog-field-binding.json" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + }, + "max_items": { + "description": "Maximum number of items the format can render. Items beyond this limit are ignored. Useful for fixed-slot layouts (e.g., a 3-product card) or feed-size constraints.", + "minimum": 1, + "type": "integer" + }, "min_items": { "description": "Minimum number of items the catalog must contain for this format to render properly (e.g., a carousel might require at least 3 products)", "minimum": 1, "type": "integer" }, "offering_asset_constraints": { - "description": "Per-offering creative requirements. Only applicable when catalog_type is 'offering'. Declares what asset groups (headlines, images, videos) each offering must provide, along with count bounds and per-asset technical constraints.", + "description": "Per-item creative asset requirements. Declares what asset groups (headlines, images, videos) each catalog item must provide in its assets array, along with count bounds and per-asset technical constraints. Applicable to 'offering' and all vertical catalog types (hotel, flight, job, etc.) whose items carry typed assets.", "items": { "$ref": "offering-asset-constraint.json" }, diff --git a/schemas/cache/core/signal-filters.json b/schemas/cache/core/signal-filters.json index 43aab063..436b610c 100644 --- a/schemas/cache/core/signal-filters.json +++ b/schemas/cache/core/signal-filters.json @@ -20,7 +20,13 @@ "type": "array" }, "max_cpm": { - "description": "Maximum CPM price filter", + "description": "Maximum CPM filter. Applies only to signals with model='cpm'.", + "minimum": 0, + "type": "number" + }, + "max_percent": { + "description": "Maximum percent-of-media rate filter. Signals where all percent_of_media pricing options exceed this value are excluded. Does not account for max_cpm caps.", + "maximum": 100, "minimum": 0, "type": "number" }, diff --git a/schemas/cache/core/signal-pricing-option.json b/schemas/cache/core/signal-pricing-option.json new file mode 100644 index 00000000..9c300738 --- /dev/null +++ b/schemas/cache/core/signal-pricing-option.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "allOf": [ + { + "properties": { + "pricing_option_id": { + "description": "Opaque identifier for this pricing option, unique within the signals agent. Pass this in report_usage to identify which pricing option was applied.", + "type": "string" + } + }, + "required": [ + "pricing_option_id" + ], + "type": "object" + }, + { + "$ref": "signal-pricing.json" + } + ], + "description": "A pricing option offered by a signals agent. Combines pricing_option_id with the signal pricing model fields at the same level \u2014 pass pricing_option_id in report_usage for billing verification.", + "title": "Signal Pricing Option" +} \ No newline at end of file diff --git a/schemas/cache/core/signal-pricing.json b/schemas/cache/core/signal-pricing.json new file mode 100644 index 00000000..3afa5fcb --- /dev/null +++ b/schemas/cache/core/signal-pricing.json @@ -0,0 +1,115 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Pricing model for a signal. Discriminated by model: 'cpm' (fixed CPM), 'percent_of_media' (percentage of spend with optional CPM cap), or 'flat_fee' (fixed charge per reporting period, e.g. monthly licensed segments).", + "oneOf": [ + { + "additionalProperties": true, + "description": "Fixed cost per thousand impressions", + "properties": { + "cpm": { + "description": "Cost per thousand impressions", + "minimum": 0, + "type": "number" + }, + "currency": { + "description": "ISO 4217 currency code", + "pattern": "^[A-Z]{3}$", + "type": "string" + }, + "ext": { + "$ref": "ext.json" + }, + "model": { + "const": "cpm", + "type": "string" + } + }, + "required": [ + "model", + "cpm", + "currency" + ], + "title": "CpmPricing", + "type": "object" + }, + { + "additionalProperties": true, + "description": "Percentage of media spend charged for this signal. When max_cpm is set, the effective rate is capped at that CPM \u2014 useful for platforms like The Trade Desk that use percent-of-media pricing with a CPM ceiling.", + "properties": { + "currency": { + "description": "ISO 4217 currency code for the resulting charge", + "pattern": "^[A-Z]{3}$", + "type": "string" + }, + "ext": { + "$ref": "ext.json" + }, + "max_cpm": { + "description": "Optional CPM cap. When set, the effective charge is min(percent \u00d7 media_spend_per_mille, max_cpm).", + "minimum": 0, + "type": "number" + }, + "model": { + "const": "percent_of_media", + "type": "string" + }, + "percent": { + "description": "Percentage of media spend, e.g. 15 = 15%", + "maximum": 100, + "minimum": 0, + "type": "number" + } + }, + "required": [ + "model", + "percent", + "currency" + ], + "title": "PercentOfMediaPricing", + "type": "object" + }, + { + "additionalProperties": true, + "description": "Fixed charge per billing period, regardless of impressions or spend. Used for licensed data bundles and audience subscriptions.", + "properties": { + "amount": { + "description": "Fixed charge for the billing period", + "minimum": 0, + "type": "number" + }, + "currency": { + "description": "ISO 4217 currency code", + "pattern": "^[A-Z]{3}$", + "type": "string" + }, + "ext": { + "$ref": "ext.json" + }, + "model": { + "const": "flat_fee", + "type": "string" + }, + "period": { + "description": "Billing period for the flat fee.", + "enum": [ + "monthly", + "quarterly", + "annual", + "campaign" + ], + "type": "string" + } + }, + "required": [ + "model", + "amount", + "period", + "currency" + ], + "title": "FlatFeePricing", + "type": "object" + } + ], + "title": "Signal Pricing", + "type": "object" +} \ No newline at end of file diff --git a/schemas/cache/core/targeting.json b/schemas/cache/core/targeting.json index fecc3e34..8c798c10 100644 --- a/schemas/cache/core/targeting.json +++ b/schemas/cache/core/targeting.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, - "description": "Optional restriction overlays for media buys. Most targeting should be expressed in the brief and handled by the publisher. These fields are for functional restrictions: geographic (RCT testing, regulatory compliance), age verification (alcohol, gambling), device platform (app compatibility), and language (localization).", + "description": "Optional restriction overlays for media buys. Most targeting should be expressed in the brief and handled by the publisher. These fields are for functional restrictions: geographic (RCT testing, regulatory compliance, proximity targeting), age verification (alcohol, gambling), device platform (app compatibility), language (localization), and keyword targeting (search/retail media).", "properties": { "age_restriction": { "additionalProperties": false, @@ -72,6 +72,22 @@ "minItems": 1, "type": "array" }, + "device_type": { + "description": "Restrict to specific device form factors. Use for campaigns targeting hardware categories rather than operating systems (e.g., mobile-only promotions, CTV campaigns).", + "items": { + "$ref": "../enums/device-type.json" + }, + "minItems": 1, + "type": "array" + }, + "device_type_exclude": { + "description": "Exclude specific device form factors from delivery (e.g., exclude CTV for app-install campaigns).", + "items": { + "$ref": "../enums/device-type.json" + }, + "minItems": 1, + "type": "array" + }, "frequency_cap": { "$ref": "frequency-cap.json" }, @@ -201,6 +217,171 @@ "minItems": 1, "type": "array" }, + "geo_proximity": { + "description": "Target users within travel time, distance, or a custom boundary around arbitrary geographic points. Multiple entries use OR semantics \u2014 a user within range of any listed point is eligible. For campaigns targeting 10+ locations, consider using store_catchments with a location catalog instead. Seller must declare support in get_adcp_capabilities.", + "items": { + "additionalProperties": true, + "oneOf": [ + { + "not": { + "anyOf": [ + { + "required": [ + "radius" + ] + }, + { + "required": [ + "geometry" + ] + } + ] + }, + "required": [ + "lat", + "lng", + "travel_time", + "transport_mode" + ] + }, + { + "not": { + "anyOf": [ + { + "required": [ + "travel_time" + ] + }, + { + "required": [ + "geometry" + ] + } + ] + }, + "required": [ + "lat", + "lng", + "radius" + ] + }, + { + "not": { + "anyOf": [ + { + "required": [ + "travel_time" + ] + }, + { + "required": [ + "radius" + ] + } + ] + }, + "required": [ + "geometry" + ] + } + ], + "properties": { + "ext": { + "$ref": "ext.json" + }, + "geometry": { + "additionalProperties": false, + "description": "Pre-computed GeoJSON geometry defining the proximity boundary. Use when the buyer has already calculated isochrones (via TravelTime, Mapbox, etc.) or has custom boundaries. When geometry is provided, lat/lng are not required.", + "properties": { + "coordinates": { + "description": "GeoJSON coordinates array. For Polygon: array of linear rings. For MultiPolygon: array of polygons.", + "type": "array" + }, + "type": { + "description": "GeoJSON geometry type.", + "enum": [ + "Polygon", + "MultiPolygon" + ], + "type": "string" + } + }, + "required": [ + "type", + "coordinates" + ], + "type": "object" + }, + "label": { + "description": "Human-readable label for this entry (e.g., 'D\u00fcsseldorf', 'Heathrow Airport', 'Primary trade area').", + "type": "string" + }, + "lat": { + "description": "Latitude in decimal degrees (WGS 84). Required for travel_time and radius methods.", + "maximum": 90, + "minimum": -90, + "type": "number" + }, + "lng": { + "description": "Longitude in decimal degrees (WGS 84). Required for travel_time and radius methods.", + "maximum": 180, + "minimum": -180, + "type": "number" + }, + "radius": { + "additionalProperties": false, + "description": "Simple radius from the point. The platform draws a circle of this distance around the coordinates.", + "properties": { + "unit": { + "$ref": "../enums/distance-unit.json", + "description": "Distance unit." + }, + "value": { + "description": "Radius distance.", + "exclusiveMinimum": 0, + "type": "number" + } + }, + "required": [ + "value", + "unit" + ], + "type": "object" + }, + "transport_mode": { + "$ref": "../enums/transport-mode.json", + "description": "Transportation mode for isochrone calculation. Required when travel_time is provided." + }, + "travel_time": { + "additionalProperties": false, + "description": "Travel time limit for isochrone calculation. The platform resolves this to a geographic boundary based on actual transportation networks.", + "properties": { + "unit": { + "description": "Time unit.", + "enum": [ + "min", + "hr" + ], + "type": "string" + }, + "value": { + "description": "Travel time limit.", + "minimum": 1, + "type": "number" + } + }, + "required": [ + "value", + "unit" + ], + "type": "object" + } + }, + "type": "object" + }, + "minItems": 1, + "type": "array" + }, "geo_regions": { "description": "Restrict delivery to specific regions/states. ISO 3166-2 subdivision codes (e.g., 'US-CA', 'GB-SCT').", "items": { @@ -219,6 +400,40 @@ "minItems": 1, "type": "array" }, + "keyword_targets": { + "description": "Keyword targeting for search and retail media platforms. Restricts delivery to queries matching the specified keywords. Each keyword is identified by the tuple (keyword, match_type) \u2014 the same keyword string with different match types are distinct targets. Sellers SHOULD reject duplicate (keyword, match_type) pairs within a single request. Seller must declare support in get_adcp_capabilities.", + "items": { + "additionalProperties": false, + "properties": { + "bid_price": { + "description": "Per-keyword bid price, denominated in the same currency as the package's pricing option. Overrides the package-level bid_price for this keyword. Inherits the max_bid interpretation from the pricing option: when max_bid is true, this is the keyword's bid ceiling; when false, this is the exact bid. If omitted, the package bid_price applies.", + "minimum": 0, + "type": "number" + }, + "keyword": { + "description": "The keyword to target", + "minLength": 1, + "type": "string" + }, + "match_type": { + "description": "Match type: broad matches related queries, phrase matches queries containing the keyword phrase, exact matches the query exactly", + "enum": [ + "broad", + "phrase", + "exact" + ], + "type": "string" + } + }, + "required": [ + "keyword", + "match_type" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, "language": { "description": "Restrict to users with specific language preferences. ISO 639-1 codes (e.g., 'en', 'es', 'fr').", "items": { @@ -228,6 +443,35 @@ "minItems": 1, "type": "array" }, + "negative_keywords": { + "description": "Keywords to exclude from delivery. Queries matching these keywords will not trigger the ad. Each negative keyword is identified by the tuple (keyword, match_type). Seller must declare support in get_adcp_capabilities.", + "items": { + "additionalProperties": false, + "properties": { + "keyword": { + "description": "The keyword to exclude", + "minLength": 1, + "type": "string" + }, + "match_type": { + "description": "Match type for exclusion", + "enum": [ + "broad", + "phrase", + "exact" + ], + "type": "string" + } + }, + "required": [ + "keyword", + "match_type" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, "property_list": { "$ref": "property-list-ref.json", "description": "Reference to a property list for targeting specific properties within this product. The package runs on the intersection of the product's publisher_properties and this list. Sellers SHOULD return a validation error if the product has property_targeting_allowed: false." diff --git a/schemas/cache/core/vehicle-item.json b/schemas/cache/core/vehicle-item.json index d8685360..0bee4d6c 100644 --- a/schemas/cache/core/vehicle-item.json +++ b/schemas/cache/core/vehicle-item.json @@ -57,6 +57,14 @@ } ], "properties": { + "assets": { + "description": "Typed creative asset pools for this vehicle. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (exterior hero), 'images_vertical' (9:16 for Stories), 'images_square' (1:1). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + "items": { + "$ref": "offering-asset-group.json" + }, + "minItems": 1, + "type": "array" + }, "body_style": { "description": "Vehicle body style.", "enum": [ diff --git a/schemas/cache/creative/asset-types/index.json b/schemas/cache/creative/asset-types/index.json index be007100..9805bf0c 100644 --- a/schemas/cache/creative/asset-types/index.json +++ b/schemas/cache/creative/asset-types/index.json @@ -28,6 +28,16 @@ "schema": "/schemas/core/assets/audio-asset.json", "typical_use": "Audio ads for streaming, podcasts, radio" }, + "brief": { + "description": "Campaign-level creative context (creative brief)", + "schema": "/schemas/core/assets/brief-asset.json", + "typical_use": "Campaign briefs with objective, messaging, compliance requirements" + }, + "catalog": { + "description": "Typed data feed (products, stores, jobs, etc.)", + "schema": "/schemas/core/assets/catalog-asset.json", + "typical_use": "Product catalogs, store locators, job feeds for dynamic creatives" + }, "css": { "description": "CSS stylesheet asset for styling", "schema": "/schemas/core/assets/css-asset.json", @@ -86,7 +96,7 @@ }, "baseUrl": "/schemas/latest", "description": "Registry of asset types used in AdCP creative manifests. Each asset type defines the structure of actual content payloads (what you send), not requirements or constraints (which belong in format specifications).", - "lastUpdated": "2026-02-22", + "lastUpdated": "2026-02-28", "title": "AdCP Asset Type Registry", "usage_notes": { "creative_manifests": "Creative manifests provide actual asset content, keyed by asset_id from the format. Asset type is determined by the format specification, not declared in the payload.", diff --git a/schemas/cache/creative/creative-feature-result.json b/schemas/cache/creative/creative-feature-result.json index d0631a3f..7b5fa0d3 100644 --- a/schemas/cache/creative/creative-feature-result.json +++ b/schemas/cache/creative/creative-feature-result.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/creative/creative-feature-result.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "description": "A single feature evaluation result for a creative. Uses the same value structure as property-feature-value (value, confidence, expires_at, etc.).", @@ -21,7 +20,7 @@ "type": "string" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "feature_id": { "description": "The feature that was evaluated (e.g., 'auto_redirect', 'brand_consistency', 'iab_casinos_gambling')", diff --git a/schemas/cache/creative/get-creative-delivery-request.json b/schemas/cache/creative/get-creative-delivery-request.json index 285ac7a6..b82979ae 100644 --- a/schemas/cache/creative/get-creative-delivery-request.json +++ b/schemas/cache/creative/get-creative-delivery-request.json @@ -20,9 +20,9 @@ ], "description": "Request parameters for retrieving creative delivery data including variant-level metrics from a creative agent. At least one scoping filter (media_buy_ids, media_buy_buyer_refs, or creative_ids) is required.", "properties": { - "account_id": { - "description": "Account context for routing and scoping. Limits results to creatives within this account. Optional if the agent has a single account or can determine routing from the media buy identifiers.", - "type": "string" + "account": { + "$ref": "../core/account-ref.json", + "description": "Account for routing and scoping. Limits results to creatives within this account." }, "context": { "$ref": "../core/context.json" @@ -65,24 +65,8 @@ "type": "array" }, "pagination": { - "additionalProperties": true, - "description": "Pagination parameters for the creatives array in the response. When omitted, the agent returns all matching creatives.", - "properties": { - "limit": { - "default": 50, - "description": "Maximum number of creatives to return", - "maximum": 100, - "minimum": 1, - "type": "integer" - }, - "offset": { - "default": 0, - "description": "Number of creatives to skip", - "minimum": 0, - "type": "integer" - } - }, - "type": "object" + "$ref": "../core/pagination-request.json", + "description": "Pagination parameters for the creatives array in the response. Uses cursor-based pagination consistent with other list operations." }, "start_date": { "description": "Start date for delivery period (YYYY-MM-DD). Interpreted in the platform's reporting timezone.", diff --git a/schemas/cache/creative/get-creative-features-request.json b/schemas/cache/creative/get-creative-features-request.json index b67eb031..4525373c 100644 --- a/schemas/cache/creative/get-creative-features-request.json +++ b/schemas/cache/creative/get-creative-features-request.json @@ -1,18 +1,21 @@ { - "$id": "/schemas/latest/creative/get-creative-features-request.json", "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "description": "Request payload for get_creative_features task. Evaluates a creative manifest and returns feature values from a creative governance agent.", + "additionalProperties": true, + "description": "Request payload for the get_creative_features task. Submits a creative manifest for evaluation by a governance agent, which analyzes the creative and returns scored feature values (brand safety, content categorization, quality metrics, etc.).", "properties": { + "account": { + "$ref": "../core/account-ref.json", + "description": "Account for billing this evaluation. Required when the governance agent charges per evaluation." + }, "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "creative_manifest": { - "$ref": "/schemas/latest/core/creative-manifest.json", + "$ref": "../core/creative-manifest.json", "description": "The creative manifest to evaluate. Contains format_id and assets." }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "feature_ids": { "description": "Optional filter to specific features. If omitted, returns all available features.", diff --git a/schemas/cache/creative/get-creative-features-response.json b/schemas/cache/creative/get-creative-features-response.json index 25bb6ea2..56f9e8b2 100644 --- a/schemas/cache/creative/get-creative-features-response.json +++ b/schemas/cache/creative/get-creative-features-response.json @@ -1,30 +1,25 @@ { - "$id": "/schemas/latest/creative/get-creative-features-response.json", "$schema": "http://json-schema.org/draft-07/schema#", - "description": "Response payload for get_creative_features task. Returns feature values for the evaluated creative.", + "description": "Response payload for the get_creative_features task. Returns scored feature values from the governance agent's evaluation of the submitted creative manifest.", "oneOf": [ { "description": "Success response", "properties": { "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "detail_url": { "description": "URL to the vendor's full assessment report. The vendor controls what information is disclosed and access control.", "format": "uri", "type": "string" }, - "errors": { - "description": "Field must not be present in success response", - "not": {} - }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "results": { "description": "Feature values for the evaluated creative", "items": { - "$ref": "/schemas/latest/creative/creative-feature-result.json" + "$ref": "creative-feature-result.json" }, "type": "array" } @@ -38,20 +33,16 @@ "description": "Error response", "properties": { "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "errors": { "items": { - "$ref": "/schemas/latest/core/error.json" + "$ref": "../core/error.json" }, "type": "array" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" - }, - "results": { - "description": "Field must not be present in error response", - "not": {} + "$ref": "../core/ext.json" } }, "required": [ diff --git a/schemas/cache/creative/preview-creative-request.json b/schemas/cache/creative/preview-creative-request.json index 1e9bf422..9cd2637b 100644 --- a/schemas/cache/creative/preview-creative-request.json +++ b/schemas/cache/creative/preview-creative-request.json @@ -11,7 +11,7 @@ }, "creative_manifest": { "$ref": "../core/creative-manifest.json", - "description": "Complete creative manifest with all required assets for the format" + "description": "Complete creative manifest with all required assets for the format." }, "ext": { "$ref": "../core/ext.json" @@ -97,7 +97,7 @@ "properties": { "creative_manifest": { "$ref": "../core/creative-manifest.json", - "description": "Complete creative manifest with all required assets" + "description": "Complete creative manifest with all required assets." }, "format_id": { "$ref": "../core/format-id.json", diff --git a/schemas/cache/creative/preview-creative-response.json b/schemas/cache/creative/preview-creative-response.json index 522f79af..33301b56 100644 --- a/schemas/cache/creative/preview-creative-response.json +++ b/schemas/cache/creative/preview-creative-response.json @@ -110,6 +110,7 @@ "properties": { "success": { "const": true, + "description": "Indicates this preview request succeeded", "type": "boolean" } }, @@ -122,43 +123,34 @@ "properties": { "success": { "const": false, + "description": "Indicates this preview request failed", "type": "boolean" } }, "required": [ - "error" + "errors" ], "title": "PreviewBatchResultError" } ], "properties": { - "error": { - "description": "Error information for failed requests", - "properties": { - "code": { - "description": "Error code (e.g., 'invalid_manifest', 'unsupported_format', 'missing_assets')", - "type": "string" - }, - "details": { - "additionalProperties": true, - "description": "Additional error context", - "type": "object" - }, - "message": { - "description": "Human-readable error message", - "type": "string" - } + "creative_id": { + "description": "ID of the creative this result corresponds to. Enables correlation when processing batch results.", + "type": "string" + }, + "errors": { + "description": "Errors for failed requests", + "items": { + "$ref": "../core/error.json" }, - "required": [ - "code", - "message" - ], - "type": "object" + "minItems": 1, + "type": "array" }, "response": { - "description": "Preview response for successful requests", + "description": "Preview data for successful requests", "properties": { "expires_at": { + "description": "When the preview URLs expire", "format": "date-time", "type": "string" }, @@ -224,7 +216,8 @@ } }, "required": [ - "success" + "success", + "creative_id" ], "type": "object" }, diff --git a/schemas/cache/enums/asset-content-type.json b/schemas/cache/enums/asset-content-type.json index d43346ff..31efb145 100644 --- a/schemas/cache/enums/asset-content-type.json +++ b/schemas/cache/enums/asset-content-type.json @@ -13,7 +13,9 @@ "vast", "daast", "url", - "webhook" + "webhook", + "brief", + "catalog" ], "title": "Asset Content Type", "type": "string" diff --git a/schemas/cache/enums/audience-source.json b/schemas/cache/enums/audience-source.json new file mode 100644 index 00000000..cb8c608f --- /dev/null +++ b/schemas/cache/enums/audience-source.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Origin of an audience segment in delivery reporting breakdowns. Only 'synced' audiences are directly targetable via the targeting overlay; other sources are informational.", + "enum": [ + "synced", + "platform", + "third_party", + "lookalike", + "retargeting", + "unknown" + ], + "title": "Audience Source", + "type": "string" +} \ No newline at end of file diff --git a/schemas/cache/enums/catalog-type.json b/schemas/cache/enums/catalog-type.json index 19e6ae0d..7811984f 100644 --- a/schemas/cache/enums/catalog-type.json +++ b/schemas/cache/enums/catalog-type.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/enums/catalog-type.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "The type of catalog feed. Determines the item schema and how the platform resolves catalog items. Multiple catalog types can be synced to the same account and referenced together in creatives.", "enum": [ diff --git a/schemas/cache/enums/consent-basis.json b/schemas/cache/enums/consent-basis.json new file mode 100644 index 00000000..158cb68d --- /dev/null +++ b/schemas/cache/enums/consent-basis.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Common GDPR lawful bases relevant to advertising. Covers the Article 6(1) bases used in programmatic advertising contexts.", + "enum": [ + "consent", + "legitimate_interest", + "contract", + "legal_obligation" + ], + "title": "Consent Basis", + "type": "string" +} \ No newline at end of file diff --git a/schemas/cache/enums/content-id-type.json b/schemas/cache/enums/content-id-type.json index 07e6ff66..be230131 100644 --- a/schemas/cache/enums/content-id-type.json +++ b/schemas/cache/enums/content-id-type.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/enums/content-id-type.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "Identifier type used in content_ids on conversion events to match back to catalog items. Omit when the identifier type is custom or not listed here.", "enum": [ diff --git a/schemas/cache/enums/creative-approval-status.json b/schemas/cache/enums/creative-approval-status.json index 7c9d904a..47a029c0 100644 --- a/schemas/cache/enums/creative-approval-status.json +++ b/schemas/cache/enums/creative-approval-status.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/enums/creative-approval-status.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "Approval state of a creative on a specific package", "enum": [ diff --git a/schemas/cache/enums/device-type.json b/schemas/cache/enums/device-type.json new file mode 100644 index 00000000..659d91b4 --- /dev/null +++ b/schemas/cache/enums/device-type.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Device form factor categories for targeting and reporting. Complements device-platform (operating system) with hardware classification. OpenRTB mapping: 1 (Mobile/Tablet General) \u2192 mobile, 2 (PC) \u2192 desktop, 4 (Phone) \u2192 mobile, 5 (Tablet) \u2192 tablet, 6 (Connected Device) \u2192 ctv, 7 (Set Top Box) \u2192 ctv. DOOH inventory uses dooh.", + "enum": [ + "desktop", + "mobile", + "tablet", + "ctv", + "dooh", + "unknown" + ], + "title": "Device Type", + "type": "string" +} \ No newline at end of file diff --git a/schemas/cache/enums/digital-source-type.json b/schemas/cache/enums/digital-source-type.json new file mode 100644 index 00000000..b0ce3a7e --- /dev/null +++ b/schemas/cache/enums/digital-source-type.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Classification of AI involvement in content creation, aligned with IPTC digitalsourcetype vocabulary. Used in provenance metadata to declare how a creative asset, artifact, or individual asset was produced.", + "enum": [ + "digital_capture", + "digital_creation", + "trained_algorithmic_media", + "composite_with_trained_algorithmic_media", + "algorithmic_media", + "composite_capture", + "composite_synthetic", + "human_edits", + "data_driven_media" + ], + "enumDescriptions": { + "algorithmic_media": "Produced by deterministic algorithms without machine learning (procedural generation, rule-based systems)", + "composite_capture": "Multiple digital captures composited together without AI", + "composite_synthetic": "Composite of multiple elements where at least one is AI-generated (e.g., stock photo composited with AI-generated background)", + "composite_with_trained_algorithmic_media": "Human-created content combined with AI-generated elements (e.g., photo with AI background)", + "data_driven_media": "Assembled from structured data feeds (DCO templates, product catalogs, weather-triggered variants)", + "digital_capture": "Captured by a digital device (camera, scanner, screen recording) with no AI involvement", + "digital_creation": "Created by a human using digital tools (Photoshop, Illustrator, After Effects) without AI generation", + "human_edits": "Content augmented, corrected, or enhanced by humans using non-generative tools", + "trained_algorithmic_media": "Generated entirely by a trained AI model (DALL-E, Midjourney, Stable Diffusion, Sora)" + }, + "title": "Digital Source Type", + "type": "string" +} \ No newline at end of file diff --git a/schemas/cache/enums/disclosure-position.json b/schemas/cache/enums/disclosure-position.json new file mode 100644 index 00000000..60517955 --- /dev/null +++ b/schemas/cache/enums/disclosure-position.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Where a required disclosure should appear within a creative. Used by creative briefs to specify disclosure placement and by formats to declare which positions they can render.", + "enum": [ + "prominent", + "footer", + "audio", + "subtitle", + "overlay", + "end_card", + "pre_roll", + "companion" + ], + "title": "Disclosure Position", + "type": "string" +} \ No newline at end of file diff --git a/schemas/cache/enums/error-code.json b/schemas/cache/enums/error-code.json new file mode 100644 index 00000000..0a79c29c --- /dev/null +++ b/schemas/cache/enums/error-code.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Standard error code vocabulary for AdCP. Codes are machine-readable so agents can apply autonomous recovery strategies based on the recovery classification. Sellers MAY return codes not listed here for platform-specific errors \u2014 the error.json code field accepts any string. Agents MUST handle unknown codes by falling back to the recovery classification.", + "enum": [ + "INVALID_REQUEST", + "AUTH_REQUIRED", + "RATE_LIMITED", + "SERVICE_UNAVAILABLE", + "POLICY_VIOLATION", + "PRODUCT_NOT_FOUND", + "PRODUCT_UNAVAILABLE", + "PROPOSAL_EXPIRED", + "BUDGET_TOO_LOW", + "CREATIVE_REJECTED", + "UNSUPPORTED_FEATURE", + "AUDIENCE_TOO_SMALL", + "ACCOUNT_NOT_FOUND", + "ACCOUNT_SETUP_REQUIRED", + "ACCOUNT_AMBIGUOUS", + "ACCOUNT_PAYMENT_REQUIRED", + "ACCOUNT_SUSPENDED", + "COMPLIANCE_UNSATISFIED", + "BUDGET_EXHAUSTED", + "CONFLICT" + ], + "enumDescriptions": { + "ACCOUNT_AMBIGUOUS": "Natural key resolves to multiple accounts. Recovery: correctable (pass explicit account_id or a more specific natural key).", + "ACCOUNT_NOT_FOUND": "The account reference could not be resolved. Recovery: terminal (verify account via list_accounts or contact seller).", + "ACCOUNT_PAYMENT_REQUIRED": "Account has an outstanding balance requiring payment before new buys. Recovery: terminal (buyer must resolve billing).", + "ACCOUNT_SETUP_REQUIRED": "Natural key resolved but the account needs setup before use. Recovery: correctable (check details.setup for URL or instructions).", + "ACCOUNT_SUSPENDED": "Account has been suspended. Recovery: terminal (contact seller to resolve suspension).", + "AUDIENCE_TOO_SMALL": "Audience segment is below the minimum required size for targeting. Recovery: correctable (broaden targeting or upload more audience members).", + "AUTH_REQUIRED": "Authentication is required to access this resource. Recovery: correctable (provide credentials via auth header).", + "BUDGET_EXHAUSTED": "Account or campaign budget has been fully spent. Distinct from BUDGET_TOO_LOW (rejected at submission). Recovery: terminal (buyer must add funds or increase budget cap).", + "BUDGET_TOO_LOW": "Budget is below the seller's minimum. Recovery: correctable (increase budget or check capabilities.media_buy.limits).", + "COMPLIANCE_UNSATISFIED": "A required disclosure from the brief's compliance section cannot be satisfied by the target format. Recovery: correctable (choose a format that supports the required disclosure positions, or remove the disclosure requirement).", + "CONFLICT": "Concurrent modification detected. The resource was modified by another request between read and write. Recovery: transient (re-read the resource and retry with current state).", + "CREATIVE_REJECTED": "Creative failed content policy review. Recovery: correctable (revise the creative per the seller's advertising_policies).", + "INVALID_REQUEST": "Request is malformed, missing required fields, or violates schema constraints. Recovery: correctable (check request parameters and fix).", + "POLICY_VIOLATION": "Request violates the seller's content or advertising policies. Recovery: correctable (review policy requirements in the error details).", + "PRODUCT_NOT_FOUND": "One or more referenced product IDs are unknown or expired. Recovery: correctable (remove invalid IDs and retry, or re-discover with get_products).", + "PRODUCT_UNAVAILABLE": "The requested product is sold out or no longer available. Recovery: correctable (choose a different product).", + "PROPOSAL_EXPIRED": "A referenced proposal ID has passed its expires_at timestamp. Recovery: correctable (re-discover with get_products to get a fresh proposal).", + "RATE_LIMITED": "Request rate exceeded. Retry after the retry_after interval. Recovery: transient.", + "SERVICE_UNAVAILABLE": "Seller service is temporarily unavailable. Retry with exponential backoff. Recovery: transient.", + "UNSUPPORTED_FEATURE": "A requested feature or field is not supported by this seller. Recovery: correctable (check get_adcp_capabilities and remove unsupported fields)." + }, + "title": "Error Code", + "type": "string" +} \ No newline at end of file diff --git a/schemas/cache/enums/forecastable-metric.json b/schemas/cache/enums/forecastable-metric.json index d1daf964..79a747f2 100644 --- a/schemas/cache/enums/forecastable-metric.json +++ b/schemas/cache/enums/forecastable-metric.json @@ -10,16 +10,24 @@ "spend", "views", "completed_views", - "grps" + "grps", + "engagements", + "follows", + "saves", + "profile_visits" ], "enumDescriptions": { "audience_size": "Estimated addressable audience size", "clicks": "Estimated clicks", - "completed_views": "Estimated 100% completions (for CPCV)", + "completed_views": "Estimated video/audio completions. Threshold depends on view_duration_seconds if set on the optimization goal.", + "engagements": "Estimated engagements (social reactions, comments, shares, story opens, overlay taps, etc.)", + "follows": "Estimated new followers, page likes, or channel subscribes", "frequency": "Estimated average frequency per individual", "grps": "Estimated Gross Rating Points (for CPP). When present, the demographic_system and demographic fields on DeliveryForecast should specify the target demographic.", "impressions": "Estimated impressions", + "profile_visits": "Estimated visits to the brand's in-platform page or profile", "reach": "Estimated unique reach. The reach_unit field on DeliveryForecast specifies the measurement unit (individuals, households, devices, etc.).", + "saves": "Estimated saves, bookmarks, playlist adds, or pins", "spend": "Estimated spend in the forecast currency", "views": "Estimated views at threshold (for CPV)" }, diff --git a/schemas/cache/enums/media-buy-status.json b/schemas/cache/enums/media-buy-status.json index 88da7e72..29958370 100644 --- a/schemas/cache/enums/media-buy-status.json +++ b/schemas/cache/enums/media-buy-status.json @@ -1,17 +1,21 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "description": "Status of a media buy", + "description": "Status of a media buy.", "enum": [ "pending_activation", "active", "paused", - "completed" + "completed", + "rejected", + "canceled" ], "enumDescriptions": { "active": "Media buy is currently running", + "canceled": "Media buy was terminated by the buyer before natural completion", "completed": "Media buy has finished running", "paused": "Media buy is temporarily paused", - "pending_activation": "Media buy created but not yet activated" + "pending_activation": "Media buy created but not yet activated", + "rejected": "Media buy was declined by the seller after creation" }, "title": "Media Buy Status", "type": "string" diff --git a/schemas/cache/enums/postal-system.json b/schemas/cache/enums/postal-system.json index 9a193a79..31e516af 100644 --- a/schemas/cache/enums/postal-system.json +++ b/schemas/cache/enums/postal-system.json @@ -10,7 +10,9 @@ "ca_full", "de_plz", "fr_code_postal", - "au_postcode" + "au_postcode", + "ch_plz", + "at_plz" ], "title": "Postal Code System", "type": "string" diff --git a/schemas/cache/enums/sort-metric.json b/schemas/cache/enums/sort-metric.json new file mode 100644 index 00000000..6fa73f34 --- /dev/null +++ b/schemas/cache/enums/sort-metric.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Numeric delivery metrics available for sorting breakdown rows. Subset of delivery-metrics fields that are flat numeric values (excludes nested objects like quartile_data, dooh_metrics, viewability, by_event_type, by_action_source).", + "enum": [ + "impressions", + "spend", + "clicks", + "ctr", + "views", + "completed_views", + "completion_rate", + "conversions", + "conversion_value", + "roas", + "cost_per_acquisition", + "new_to_brand_rate", + "leads", + "grps", + "reach", + "frequency", + "engagements", + "follows", + "saves", + "profile_visits", + "engagement_rate", + "cost_per_click" + ], + "title": "Sort Metric", + "type": "string" +} \ No newline at end of file diff --git a/schemas/cache/enums/task-type.json b/schemas/cache/enums/task-type.json index 5c205451..5b2789b6 100644 --- a/schemas/cache/enums/task-type.json +++ b/schemas/cache/enums/task-type.json @@ -13,6 +13,7 @@ "list_property_lists", "delete_property_list", "sync_accounts", + "get_account_financials", "get_creative_delivery", "sync_event_sources", "sync_audiences", @@ -24,12 +25,13 @@ "create_media_buy": "Media-buy domain: Create a new advertising campaign with one or more packages", "create_property_list": "Property domain: Create a new property list with filters and brand reference", "delete_property_list": "Property domain: Delete a property list", + "get_account_financials": "Account domain: Query financial status of an operator-billed account (spend, credit, invoices)", "get_creative_delivery": "Creative domain: Retrieve variant-level creative delivery data", "get_property_list": "Property domain: Retrieve a property list with resolved properties", "get_signals": "Signals domain: Discover available audience signals based on natural language description", "list_property_lists": "Property domain: List all accessible property lists", "log_event": "Media-buy domain: Send conversion or marketing events for attribution", - "sync_accounts": "Account domain: Sync advertiser accounts with a seller, declaring brands, operators, and billing arrangements", + "sync_accounts": "Account domain: Sync advertiser accounts with a seller using upsert semantics", "sync_audiences": "Media-buy domain: Manage first-party CRM audiences on an account with delta upsert semantics", "sync_catalogs": "Media-buy domain: Sync catalog feeds (products, inventory, stores, promotions, offerings) to a platform with approval workflow", "sync_creatives": "Media-buy domain: Sync creative assets to publisher's library with upsert semantics", diff --git a/schemas/cache/enums/uid-type.json b/schemas/cache/enums/uid-type.json index 54d69def..97757517 100644 --- a/schemas/cache/enums/uid-type.json +++ b/schemas/cache/enums/uid-type.json @@ -7,13 +7,11 @@ "uid2", "euid", "pairid", - "external_id", "maid", "other" ], "enumDescriptions": { "euid": "European Unified ID", - "external_id": "Advertiser-assigned first-party user identifier", "id5": "ID5 universal ID", "maid": "Mobile Advertising ID (IDFA/GAID)", "other": "Other universal ID type (specify in ext)", diff --git a/schemas/cache/enums/universal-macro.json b/schemas/cache/enums/universal-macro.json index ee254705..c06a69b4 100644 --- a/schemas/cache/enums/universal-macro.json +++ b/schemas/cache/enums/universal-macro.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/enums/universal-macro.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "Standardized macro placeholders for dynamic value substitution in creative tracking URLs. Macros are replaced with actual values at impression time. See docs/creative/universal-macros.mdx for detailed documentation.", "enum": [ diff --git a/schemas/cache/extensions/index.json b/schemas/cache/extensions/index.json index 37486fa9..6a48fdef 100644 --- a/schemas/cache/extensions/index.json +++ b/schemas/cache/extensions/index.json @@ -1,8 +1,7 @@ { - "$id": "/schemas/3.0.0-beta.3/extensions/index.json", "$schema": "http://json-schema.org/draft-07/schema#", "_generated": true, - "_generatedAt": "2026-02-22T23:54:26.139Z", + "_generatedAt": "2026-02-28T16:57:35.611Z", "description": "Auto-generated registry of formal AdCP extensions. Extensions provide typed schemas for vendor-specific or domain-specific data within the ext field. Agents declare which extensions they support in their agent card.", "extensions": {}, "title": "AdCP Extension Registry" diff --git a/schemas/cache/media-buy/build-creative-request.json b/schemas/cache/media-buy/build-creative-request.json index 3ad448ca..3f304c0c 100644 --- a/schemas/cache/media-buy/build-creative-request.json +++ b/schemas/cache/media-buy/build-creative-request.json @@ -10,10 +10,6 @@ "context": { "$ref": "../core/context.json" }, - "creative_brief": { - "$ref": "../core/creative-brief-ref.json", - "description": "Campaign-level creative brief with objective, audience, messaging, and reference assets. Can be an inline brief object or a URL to a hosted brief. Supplements the natural language message with structured creative direction." - }, "creative_manifest": { "$ref": "../core/creative-manifest.json", "description": "Creative manifest to transform or generate from. For pure generation, this should include the target format_id and any required input assets. For transformation (e.g., resizing, reformatting), this is the complete creative to adapt." @@ -22,7 +18,7 @@ "$ref": "../core/ext.json" }, "message": { - "description": "Natural language instructions for the transformation or generation. For pure generation, this is the creative brief. For transformation, this provides guidance on how to adapt the creative.", + "description": "Natural language instructions for the transformation or generation. For pure generation, this is the creative brief. For transformation, this provides guidance on how to adapt the creative. For refinement, this describes the desired changes.", "type": "string" }, "target_format_id": { diff --git a/schemas/cache/media-buy/create-media-buy-request.json b/schemas/cache/media-buy/create-media-buy-request.json index 7e4332ba..9eca085d 100644 --- a/schemas/cache/media-buy/create-media-buy-request.json +++ b/schemas/cache/media-buy/create-media-buy-request.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/media-buy/create-media-buy-request.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "dependencies": { @@ -9,9 +8,9 @@ }, "description": "Request parameters for creating a media buy. Supports two modes: (1) Manual mode - provide packages array with explicit line item configurations, or (2) Proposal mode - provide proposal_id and total_budget to execute a proposal from get_products. One of packages or proposal_id must be provided.", "properties": { - "account_id": { - "description": "Account to bill for this media buy. Required when the agent has access to multiple accounts; when omitted, the seller uses the agent's sole account. The seller maps the agent's brand + operator to an account during sync_accounts; the agent passes that account_id here.", - "type": "string" + "account": { + "$ref": "../core/account-ref.json", + "description": "Account to bill for this media buy. Pass an account_id from list_accounts (explicit model), or use a natural key (brand, operator) if the seller supports implicit_from_sync resolution." }, "artifact_webhook": { "$comment": "Webhook configuration for content artifact delivery - enables governance validation. Same authentication structure as reporting_webhook.", @@ -30,7 +29,7 @@ "schemes": { "description": "Array of authentication schemes. Supported: ['Bearer'] for simple token auth, ['HMAC-SHA256'] for signature verification (recommended for production)", "items": { - "$ref": "/schemas/latest/enums/auth-scheme.json" + "$ref": "../enums/auth-scheme.json" }, "maxItems": 1, "minItems": 1, @@ -84,7 +83,7 @@ "type": "object" }, "brand": { - "$ref": "/schemas/latest/core/brand-ref.json", + "$ref": "../core/brand-ref.json", "description": "Brand reference for this media buy. Resolved to full brand identity at execution time from brand.json or the registry." }, "buyer_campaign_ref": { @@ -92,11 +91,11 @@ "type": "string" }, "buyer_ref": { - "description": "Buyer's reference identifier for this media buy", + "description": "Buyer's reference identifier for this media buy. Also serves as an idempotency key: sellers SHOULD deduplicate requests with the same buyer_ref and account, returning the existing media buy rather than creating a duplicate.", "type": "string" }, "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "end_time": { "description": "Campaign end date/time in ISO 8601 format", @@ -104,12 +103,12 @@ "type": "string" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "packages": { "description": "Array of package configurations. Required when not using proposal_id. When executing a proposal, this can be omitted and packages will be derived from the proposal's allocations.", "items": { - "$ref": "/schemas/latest/media-buy/package-request.json" + "$ref": "package-request.json" }, "minItems": 1, "type": "array" @@ -122,12 +121,16 @@ "description": "ID of a proposal from get_products to execute. When provided with total_budget, the publisher converts the proposal's allocation percentages into packages automatically. Alternative to providing packages array.", "type": "string" }, + "push_notification_config": { + "$ref": "../core/push-notification-config.json", + "description": "Optional webhook configuration for async task status notifications. Publisher will send webhooks when status changes (working, input-required, completed, failed). The client generates an operation_id and embeds it in the URL before sending \u2014 the publisher echoes it back in webhook payloads for correlation." + }, "reporting_webhook": { - "$ref": "/schemas/latest/core/reporting-webhook.json", + "$ref": "../core/reporting-webhook.json", "description": "Optional webhook configuration for automated reporting delivery" }, "start_time": { - "$ref": "/schemas/latest/core/start-timing.json" + "$ref": "../core/start-timing.json" }, "total_budget": { "additionalProperties": false, @@ -152,6 +155,7 @@ }, "required": [ "buyer_ref", + "account", "brand", "start_time", "end_time" diff --git a/schemas/cache/media-buy/create-media-buy-response.json b/schemas/cache/media-buy/create-media-buy-response.json index 10473901..028191a5 100644 --- a/schemas/cache/media-buy/create-media-buy-response.json +++ b/schemas/cache/media-buy/create-media-buy-response.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/media-buy/create-media-buy-response.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "Response payload for create_media_buy task. Returns either complete success data OR error information, never both. This enforces atomic operation semantics - the media buy is either fully created or not created at all.", "oneOf": [ @@ -13,7 +12,7 @@ }, "properties": { "account": { - "$ref": "/schemas/latest/core/account.json", + "$ref": "../core/account.json", "description": "Account billed for this media buy. Includes advertiser, billing proxy (if any), and rate card applied." }, "buyer_campaign_ref": { @@ -25,7 +24,7 @@ "type": "string" }, "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "creative_deadline": { "description": "ISO 8601 timestamp for creative upload deadline", @@ -33,7 +32,7 @@ "type": "string" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "media_buy_id": { "description": "Publisher's unique identifier for the created media buy", @@ -42,7 +41,7 @@ "packages": { "description": "Array of created packages with complete state information", "items": { - "$ref": "/schemas/latest/core/package.json" + "$ref": "../core/package.json" }, "type": "array" }, @@ -88,18 +87,18 @@ }, "properties": { "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "errors": { "description": "Array of errors explaining why the operation failed", "items": { - "$ref": "/schemas/latest/core/error.json" + "$ref": "../core/error.json" }, "minItems": 1, "type": "array" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" } }, "required": [ diff --git a/schemas/cache/media-buy/get-media-buy-delivery-request.json b/schemas/cache/media-buy/get-media-buy-delivery-request.json index a8015373..89d2d88b 100644 --- a/schemas/cache/media-buy/get-media-buy-delivery-request.json +++ b/schemas/cache/media-buy/get-media-buy-delivery-request.json @@ -3,9 +3,36 @@ "additionalProperties": true, "description": "Request parameters for retrieving comprehensive delivery metrics", "properties": { - "account_id": { - "description": "Filter delivery data to a specific account. When provided, only returns media buys belonging to this account. When omitted, returns data across all accessible accounts. Optional if the agent has a single account.", - "type": "string" + "account": { + "$ref": "../core/account-ref.json", + "description": "Filter delivery data to a specific account. When omitted, returns data across all accessible accounts." + }, + "attribution_window": { + "additionalProperties": true, + "description": "Attribution window to apply for conversion metrics. When provided, the seller returns conversion data using the requested lookback windows instead of their platform default. The seller echoes the applied window in the response. Sellers that do not support configurable windows ignore this field and return their default. Check get_adcp_capabilities conversion_tracking.attribution_windows for available options.", + "properties": { + "model": { + "$ref": "../enums/attribution-model.json", + "description": "Attribution model to use. When omitted, the seller applies their default model." + }, + "post_click": { + "allOf": [ + { + "$ref": "../core/duration.json" + } + ], + "description": "Post-click attribution window to apply." + }, + "post_view": { + "allOf": [ + { + "$ref": "../core/duration.json" + } + ], + "description": "Post-view attribution window to apply." + } + }, + "type": "object" }, "buyer_refs": { "description": "Array of buyer reference IDs to get delivery data for", @@ -26,6 +53,11 @@ "ext": { "$ref": "../core/ext.json" }, + "include_package_daily_breakdown": { + "default": false, + "description": "When true, include daily_breakdown arrays within each package in by_package. Useful for per-package pacing analysis and line-item monitoring. Omit or set false to reduce response size \u2014 package daily data can be large for multi-package buys over long flights.", + "type": "boolean" + }, "media_buy_ids": { "description": "Array of publisher media buy IDs to get delivery data for", "items": { @@ -34,6 +66,119 @@ "minItems": 1, "type": "array" }, + "reporting_dimensions": { + "additionalProperties": true, + "description": "Request dimensional breakdowns in delivery reporting. Each key enables a specific breakdown dimension within by_package \u2014 include as an empty object (e.g., \"device_type\": {}) to activate with defaults. Omit entirely for no breakdowns (backward compatible). Unsupported dimensions are silently omitted from the response. Note: keyword, catalog_item, and creative breakdowns are returned automatically when the seller supports them and are not controlled by this object.", + "properties": { + "audience": { + "additionalProperties": true, + "description": "Request audience segment breakdown.", + "properties": { + "limit": { + "default": 25, + "description": "Maximum number of entries to return. Defaults to 25.", + "minimum": 1, + "type": "integer" + }, + "sort_by": { + "$ref": "../enums/sort-metric.json", + "default": "spend", + "description": "Metric to sort breakdown rows by (descending). Falls back to 'spend' if the seller does not report the requested metric." + } + }, + "type": "object" + }, + "device_platform": { + "additionalProperties": true, + "description": "Request device platform breakdown.", + "properties": { + "limit": { + "description": "Maximum number of entries to return. When omitted, all entries are returned (the enum is small and bounded).", + "minimum": 1, + "type": "integer" + }, + "sort_by": { + "$ref": "../enums/sort-metric.json", + "default": "spend", + "description": "Metric to sort breakdown rows by (descending). Falls back to 'spend' if the seller does not report the requested metric." + } + }, + "type": "object" + }, + "device_type": { + "additionalProperties": true, + "description": "Request device type breakdown.", + "properties": { + "limit": { + "description": "Maximum number of entries to return. When omitted, all entries are returned (the enum is small and bounded).", + "minimum": 1, + "type": "integer" + }, + "sort_by": { + "$ref": "../enums/sort-metric.json", + "default": "spend", + "description": "Metric to sort breakdown rows by (descending). Falls back to 'spend' if the seller does not report the requested metric." + } + }, + "type": "object" + }, + "geo": { + "additionalProperties": true, + "description": "Request geographic breakdown. Check reporting_capabilities.supports_geo_breakdown for available levels and systems.", + "properties": { + "geo_level": { + "$ref": "../enums/geo-level.json", + "description": "Geographic granularity level for the breakdown" + }, + "limit": { + "default": 25, + "description": "Maximum number of geo entries to return. Defaults to 25. When truncated, by_geo_truncated is true in the response.", + "minimum": 1, + "type": "integer" + }, + "sort_by": { + "$ref": "../enums/sort-metric.json", + "default": "spend", + "description": "Metric to sort breakdown rows by (descending). Falls back to 'spend' if the seller does not report the requested metric." + }, + "system": { + "description": "Classification system for metro or postal_area levels (e.g., 'nielsen_dma', 'us_zip'). Required when geo_level is 'metro' or 'postal_area'.", + "oneOf": [ + { + "$ref": "../enums/metro-system.json" + }, + { + "$ref": "../enums/postal-system.json" + } + ] + } + }, + "required": [ + "geo_level" + ], + "type": "object" + }, + "placement": { + "additionalProperties": true, + "description": "Request placement breakdown.", + "properties": { + "limit": { + "default": 25, + "description": "Maximum number of entries to return. Defaults to 25.", + "minimum": 1, + "type": "integer" + }, + "sort_by": { + "$ref": "../enums/sort-metric.json", + "default": "spend", + "description": "Metric to sort breakdown rows by (descending). Falls back to 'spend' if the seller does not report the requested metric." + } + }, + "type": "object" + } + }, + "type": "object" + }, "start_date": { "description": "Start date for reporting period (YYYY-MM-DD). When omitted along with end_date, returns campaign lifetime data. Only accepted when the product's reporting_capabilities.date_range_support is 'date_range'.", "pattern": "^\\d{4}-\\d{2}-\\d{2}$", diff --git a/schemas/cache/media-buy/get-media-buy-delivery-response.json b/schemas/cache/media-buy/get-media-buy-delivery-response.json index 651defc8..9e42866a 100644 --- a/schemas/cache/media-buy/get-media-buy-delivery-response.json +++ b/schemas/cache/media-buy/get-media-buy-delivery-response.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/media-buy/get-media-buy-delivery-response.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Response payload for get_media_buy_delivery task", @@ -68,11 +67,11 @@ "type": "object" }, "attribution_window": { - "$ref": "/schemas/latest/core/attribution-window.json", + "$ref": "../core/attribution-window.json", "description": "Attribution methodology and lookback windows used for conversion metrics in this response. All media buys from a single seller share the same attribution methodology. Enables cross-platform comparison (e.g., Amazon 14-day click vs. Criteo 30-day click)." }, "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "currency": { "description": "ISO 4217 currency code", @@ -82,12 +81,12 @@ "errors": { "description": "Task-specific errors and warnings (e.g., missing delivery data, reporting platform issues)", "items": { - "$ref": "/schemas/latest/core/error.json" + "$ref": "../core/error.json" }, "type": "array" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "media_buy_deliveries": { "description": "Array of delivery data for media buys. When used in webhook notifications, may contain multiple media buys aggregated by publisher. When used in get_media_buy_delivery API responses, typically contains requested media buys.", @@ -107,7 +106,7 @@ "items": { "allOf": [ { - "$ref": "/schemas/latest/core/delivery-metrics.json" + "$ref": "../core/delivery-metrics.json" }, { "properties": { @@ -115,12 +114,50 @@ "description": "Buyer's reference identifier for this package", "type": "string" }, + "by_audience": { + "description": "Delivery by audience segment within this package. Available when the buyer requests audience breakdown via reporting_dimensions and the seller supports it. Only 'synced' audiences are directly targetable via the targeting overlay; other sources are informational.", + "items": { + "allOf": [ + { + "$ref": "../core/delivery-metrics.json" + }, + { + "properties": { + "audience_id": { + "description": "Audience segment identifier. For 'synced' source, matches audience_id from sync_audiences. For other sources, seller-defined.", + "type": "string" + }, + "audience_name": { + "description": "Human-readable audience segment name", + "type": "string" + }, + "audience_source": { + "$ref": "../enums/audience-source.json", + "description": "Origin of the audience segment (synced, platform, third_party, lookalike, retargeting, unknown)" + } + }, + "required": [ + "audience_id", + "audience_source", + "impressions", + "spend" + ], + "type": "object" + } + ] + }, + "type": "array" + }, + "by_audience_truncated": { + "description": "Whether by_audience was truncated. Sellers MUST return this flag whenever by_audience is present (false means the list is complete).", + "type": "boolean" + }, "by_catalog_item": { "description": "Delivery by catalog item within this package. Available for catalog-driven packages when the seller supports item-level reporting.", "items": { "allOf": [ { - "$ref": "/schemas/latest/core/delivery-metrics.json" + "$ref": "../core/delivery-metrics.json" }, { "properties": { @@ -129,7 +166,7 @@ "type": "string" }, "content_id_type": { - "$ref": "/schemas/latest/enums/content-id-type.json", + "$ref": "../enums/content-id-type.json", "description": "Identifier type for this content_id" } }, @@ -149,7 +186,7 @@ "items": { "allOf": [ { - "$ref": "/schemas/latest/core/delivery-metrics.json" + "$ref": "../core/delivery-metrics.json" }, { "properties": { @@ -175,11 +212,230 @@ }, "type": "array" }, + "by_device_platform": { + "description": "Delivery by operating system within this package. Available when the buyer requests device_platform breakdown via reporting_dimensions and the seller supports it. Useful for CTV campaigns where tvOS vs Roku OS vs Fire OS matters.", + "items": { + "allOf": [ + { + "$ref": "../core/delivery-metrics.json" + }, + { + "properties": { + "device_platform": { + "$ref": "../enums/device-platform.json", + "description": "Operating system platform" + } + }, + "required": [ + "device_platform", + "impressions", + "spend" + ], + "type": "object" + } + ] + }, + "type": "array" + }, + "by_device_platform_truncated": { + "description": "Whether by_device_platform was truncated. Sellers MUST return this flag whenever by_device_platform is present (false means the list is complete).", + "type": "boolean" + }, + "by_device_type": { + "description": "Delivery by device form factor within this package. Available when the buyer requests device_type breakdown via reporting_dimensions and the seller supports it.", + "items": { + "allOf": [ + { + "$ref": "../core/delivery-metrics.json" + }, + { + "properties": { + "device_type": { + "$ref": "../enums/device-type.json", + "description": "Device form factor (desktop, mobile, tablet, ctv, dooh, unknown)" + } + }, + "required": [ + "device_type", + "impressions", + "spend" + ], + "type": "object" + } + ] + }, + "type": "array" + }, + "by_device_type_truncated": { + "description": "Whether by_device_type was truncated. Sellers MUST return this flag whenever by_device_type is present (false means the list is complete).", + "type": "boolean" + }, + "by_geo": { + "description": "Delivery by geographic area within this package. Available when the buyer requests geo breakdown via reporting_dimensions and the seller supports it. Each dimension's rows are independent slices that should sum to the package total.", + "items": { + "allOf": [ + { + "$ref": "../core/delivery-metrics.json" + }, + { + "properties": { + "geo_code": { + "description": "Geographic code within the level and system. Country: ISO 3166-1 alpha-2 ('US'). Region: ISO 3166-2 with country prefix ('US-CA'). Metro/postal: system-specific code ('501', '10001').", + "type": "string" + }, + "geo_level": { + "$ref": "../enums/geo-level.json", + "description": "Geographic level of this entry (country, region, metro, postal_area)" + }, + "geo_name": { + "description": "Human-readable geographic name (e.g., 'United States', 'California', 'New York DMA')", + "type": "string" + }, + "system": { + "description": "Classification system for metro or postal_area levels (e.g., 'nielsen_dma', 'us_zip'). Present when geo_level is 'metro' or 'postal_area'.", + "type": "string" + } + }, + "required": [ + "geo_level", + "geo_code", + "impressions", + "spend" + ], + "type": "object" + } + ] + }, + "type": "array" + }, + "by_geo_truncated": { + "description": "Whether by_geo was truncated due to the requested limit or a seller-imposed maximum. Sellers MUST return this flag whenever by_geo is present (false means the list is complete).", + "type": "boolean" + }, + "by_keyword": { + "description": "Metrics broken down by keyword within this package. One row per (keyword, match_type) pair \u2014 the same keyword with different match types appears as separate rows. Keyword-grain only: rows reflect aggregate performance of each targeted keyword, not individual search queries. Rows may not sum to package totals when a single impression is attributed to the triggering keyword only. Available for search and retail media packages when the seller supports keyword-level reporting.", + "items": { + "allOf": [ + { + "$ref": "../core/delivery-metrics.json" + }, + { + "properties": { + "keyword": { + "description": "The targeted keyword", + "type": "string" + }, + "match_type": { + "description": "Match type for this keyword", + "enum": [ + "broad", + "phrase", + "exact" + ], + "type": "string" + } + }, + "required": [ + "keyword", + "match_type", + "impressions", + "spend" + ], + "type": "object" + } + ] + }, + "type": "array" + }, + "by_placement": { + "description": "Delivery by placement within this package. Available when the buyer requests placement breakdown via reporting_dimensions and the seller supports it. Placement IDs reference the product's placements array.", + "items": { + "allOf": [ + { + "$ref": "../core/delivery-metrics.json" + }, + { + "properties": { + "placement_id": { + "description": "Placement identifier from the product's placements array", + "type": "string" + }, + "placement_name": { + "description": "Human-readable placement name", + "type": "string" + } + }, + "required": [ + "placement_id", + "impressions", + "spend" + ], + "type": "object" + } + ] + }, + "type": "array" + }, + "by_placement_truncated": { + "description": "Whether by_placement was truncated. Sellers MUST return this flag whenever by_placement is present (false means the list is complete).", + "type": "boolean" + }, "currency": { "description": "ISO 4217 currency code (e.g., USD, EUR, GBP) for this package's pricing. Indicates the currency in which the rate and spend values are denominated. Different packages can use different currencies when supported by the publisher.", "pattern": "^[A-Z]{3}$", "type": "string" }, + "daily_breakdown": { + "description": "Day-by-day delivery for this package. Only present when include_package_daily_breakdown is true in the request. Enables per-package pacing analysis and line-item monitoring.", + "items": { + "additionalProperties": true, + "properties": { + "conversion_value": { + "description": "Daily conversion value for this package", + "minimum": 0, + "type": "number" + }, + "conversions": { + "description": "Daily conversions for this package", + "minimum": 0, + "type": "number" + }, + "date": { + "description": "Date (YYYY-MM-DD)", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "impressions": { + "description": "Daily impressions for this package", + "minimum": 0, + "type": "number" + }, + "new_to_brand_rate": { + "description": "Daily fraction of conversions from first-time brand buyers (0 = none, 1 = all)", + "maximum": 1, + "minimum": 0, + "type": "number" + }, + "roas": { + "description": "Daily return on ad spend (conversion_value / spend)", + "minimum": 0, + "type": "number" + }, + "spend": { + "description": "Daily spend for this package", + "minimum": 0, + "type": "number" + } + }, + "required": [ + "date", + "impressions", + "spend" + ], + "type": "object" + }, + "type": "array" + }, "delivery_status": { "description": "System-reported operational state of this package. Reflects actual delivery state independent of buyer pause control.", "enum": [ @@ -205,7 +461,7 @@ "type": "boolean" }, "pricing_model": { - "$ref": "/schemas/latest/enums/pricing-model.json", + "$ref": "../enums/pricing-model.json", "description": "The pricing model used for this package (e.g., cpm, cpcv, cpp). Indicates how the package is billed and which metrics are most relevant for optimization." }, "rate": { @@ -292,17 +548,19 @@ "type": "string" }, "pricing_model": { - "$ref": "/schemas/latest/enums/pricing-model.json", + "$ref": "../enums/pricing-model.json", "description": "Pricing model used for this media buy" }, "status": { - "description": "Current media buy status. Lifecycle states use the same taxonomy as media-buy-status (`pending_activation`, `active`, `paused`, `completed`). In webhook context, reporting_delayed indicates data temporarily unavailable. `pending` is accepted as a legacy alias for pending_activation.", + "description": "Current media buy status. Lifecycle states use the same taxonomy as media-buy-status (`pending_activation`, `active`, `paused`, `completed`, `rejected`, `canceled`). In webhook context, reporting_delayed indicates data temporarily unavailable. `pending` is accepted as a legacy alias for pending_activation.", "enum": [ "pending_activation", "pending", "active", "paused", "completed", + "rejected", + "canceled", "failed", "reporting_delayed" ], @@ -311,7 +569,7 @@ "totals": { "allOf": [ { - "$ref": "/schemas/latest/core/delivery-metrics.json" + "$ref": "../core/delivery-metrics.json" }, { "description": "Aggregate metrics for this media buy across all packages", diff --git a/schemas/cache/media-buy/get-media-buys-request.json b/schemas/cache/media-buy/get-media-buys-request.json index db3a5a13..a558dd88 100644 --- a/schemas/cache/media-buy/get-media-buys-request.json +++ b/schemas/cache/media-buy/get-media-buys-request.json @@ -1,12 +1,11 @@ { - "$id": "/schemas/latest/media-buy/get-media-buys-request.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Request parameters for retrieving media buy status, creative approval state, and optional delivery snapshots", "properties": { - "account_id": { - "description": "Filter to a specific account. When omitted, returns media buys across all accessible accounts. Optional if the agent has a single account.", - "type": "string" + "account": { + "$ref": "../core/account-ref.json", + "description": "Account to retrieve media buys for." }, "buyer_refs": { "description": "Array of buyer reference IDs to retrieve", @@ -17,10 +16,10 @@ "type": "array" }, "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "include_snapshot": { "default": false, @@ -36,18 +35,18 @@ "type": "array" }, "pagination": { - "$ref": "/schemas/latest/core/pagination-request.json", + "$ref": "../core/pagination-request.json", "description": "Cursor-based pagination controls. Strongly recommended when querying broad scopes (for example, all active media buys in an account)." }, "status_filter": { "description": "Filter by status. Can be a single status or array of statuses. Defaults to [\"active\"] only when media_buy_ids and buyer_refs are both omitted. When media_buy_ids or buyer_refs are provided, no implicit status filter is applied.", "oneOf": [ { - "$ref": "/schemas/latest/enums/media-buy-status.json" + "$ref": "../enums/media-buy-status.json" }, { "items": { - "$ref": "/schemas/latest/enums/media-buy-status.json" + "$ref": "../enums/media-buy-status.json" }, "minItems": 1, "type": "array" @@ -55,6 +54,9 @@ ] } }, + "required": [ + "account" + ], "title": "Get Media Buys Request", "type": "object" } \ No newline at end of file diff --git a/schemas/cache/media-buy/get-media-buys-response.json b/schemas/cache/media-buy/get-media-buys-response.json index fbe85138..80e9124c 100644 --- a/schemas/cache/media-buy/get-media-buys-response.json +++ b/schemas/cache/media-buy/get-media-buys-response.json @@ -1,21 +1,20 @@ { - "$id": "/schemas/latest/media-buy/get-media-buys-response.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Response payload for get_media_buys task. Returns media buy configuration, creative approval state, and optional delivery snapshots.", "properties": { "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "errors": { "description": "Task-specific errors (e.g., media buy not found)", "items": { - "$ref": "/schemas/latest/core/error.json" + "$ref": "../core/error.json" }, "type": "array" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "media_buys": { "description": "Array of media buys with status, creative approval state, and optional delivery snapshots", @@ -23,7 +22,7 @@ "additionalProperties": true, "properties": { "account": { - "$ref": "/schemas/latest/core/account.json", + "$ref": "../core/account.json", "description": "Account billed for this media buy" }, "buyer_campaign_ref": { @@ -49,8 +48,13 @@ "pattern": "^[A-Z]{3}$", "type": "string" }, + "end_time": { + "description": "ISO 8601 flight end time for this media buy (latest package end_time). Avoids requiring buyers to compute max(packages[].end_time).", + "format": "date-time", + "type": "string" + }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "media_buy_id": { "description": "Publisher's unique identifier for the media buy", @@ -81,7 +85,7 @@ "additionalProperties": true, "properties": { "approval_status": { - "$ref": "/schemas/latest/enums/creative-approval-status.json" + "$ref": "../enums/creative-approval-status.json" }, "creative_id": { "description": "Creative identifier", @@ -111,12 +115,12 @@ "type": "string" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "format_ids_pending": { "description": "Format IDs from the original create_media_buy format_ids_to_provide that have not yet been uploaded via sync_creatives. When empty or absent, all required formats have been provided.", "items": { - "$ref": "/schemas/latest/core/format-id.json" + "$ref": "../core/format-id.json" }, "type": "array" }, @@ -169,7 +173,7 @@ "type": "string" }, "ext": { - "$ref": "/schemas/latest/core/ext.json", + "$ref": "../core/ext.json", "description": "Optional extension object for seller-specific snapshot fields." }, "impressions": { @@ -223,8 +227,13 @@ }, "type": "array" }, + "start_time": { + "description": "ISO 8601 flight start time for this media buy (earliest package start_time). Avoids requiring buyers to compute min(packages[].start_time).", + "format": "date-time", + "type": "string" + }, "status": { - "$ref": "/schemas/latest/enums/media-buy-status.json" + "$ref": "../enums/media-buy-status.json" }, "total_budget": { "description": "Total budget amount across all packages, denominated in media_buy.currency", @@ -249,7 +258,7 @@ "type": "array" }, "pagination": { - "$ref": "/schemas/latest/core/pagination-response.json", + "$ref": "../core/pagination-response.json", "description": "Pagination metadata for the media_buys array." }, "sandbox": { diff --git a/schemas/cache/media-buy/get-products-request.json b/schemas/cache/media-buy/get-products-request.json index 468e9bbb..1bfb1574 100644 --- a/schemas/cache/media-buy/get-products-request.json +++ b/schemas/cache/media-buy/get-products-request.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/media-buy/get-products-request.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "dependencies": { @@ -7,46 +6,225 @@ "brand" ] }, - "description": "Request parameters for discovering available advertising products", + "description": "Request parameters for discovering or refining advertising products. buying_mode declares the buyer's intent: 'brief' for curated discovery, 'wholesale' for raw catalog access, or 'refine' to iterate on known products and proposals.", + "oneOf": [ + { + "properties": { + "buying_mode": { + "const": "brief" + }, + "refine": false + }, + "required": [ + "buying_mode", + "brief" + ] + }, + { + "properties": { + "brief": false, + "buying_mode": { + "const": "wholesale" + }, + "refine": false + }, + "required": [ + "buying_mode" + ] + }, + { + "properties": { + "brief": false, + "buying_mode": { + "const": "refine" + } + }, + "required": [ + "buying_mode", + "refine" + ] + } + ], "properties": { - "account_id": { - "description": "Account ID for product lookup. Required when the seller declares account.required_for_products = true in capabilities. Returns products with pricing specific to this account's rate card.", - "type": "string" + "account": { + "$ref": "../core/account-ref.json", + "description": "Account for product lookup. Returns products with pricing specific to this account's rate card." }, "brand": { - "$ref": "/schemas/latest/core/brand-ref.json", + "$ref": "../core/brand-ref.json", "description": "Brand reference for product discovery context. Resolved to full brand identity at execution time." }, "brief": { - "description": "Natural language description of campaign requirements.", + "description": "Natural language description of campaign requirements. Required when buying_mode is 'brief'. Must not be provided when buying_mode is 'wholesale' or 'refine'.", "type": "string" }, "buyer_campaign_ref": { "description": "Buyer's campaign reference label. Groups related discovery and buy operations under a single campaign for CRM and ad server correlation (e.g., 'NovaDrink_Meals_Q2').", "type": "string" }, + "buying_mode": { + "description": "Declares buyer intent for this request. 'brief': publisher curates product recommendations from the provided brief. 'wholesale': buyer requests raw inventory to apply their own audiences \u2014 brief must not be provided, and proposals are omitted. 'refine': iterate on products and proposals from a previous get_products response using the refine array of change requests.", + "enum": [ + "brief", + "wholesale", + "refine" + ], + "type": "string" + }, "catalog": { - "$ref": "/schemas/latest/core/catalog.json", + "$ref": "../core/catalog.json", "description": "Catalog of items the buyer wants to promote. The seller matches catalog items against its inventory and returns products where matches exist. Supports all catalog types: a job catalog finds job ad products, a product catalog finds sponsored product slots. Reference a synced catalog by catalog_id, or provide inline items." }, "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" + }, + "fields": { + "description": "Specific product fields to include in the response. When omitted, all fields are returned. Use for lightweight discovery calls where only a subset of product data is needed (e.g., just IDs and pricing for comparison). Required fields (product_id, name) are always included regardless of selection.", + "items": { + "enum": [ + "product_id", + "name", + "description", + "publisher_properties", + "channels", + "format_ids", + "placements", + "delivery_type", + "pricing_options", + "forecast", + "outcome_measurement", + "delivery_measurement", + "reporting_capabilities", + "creative_policy", + "catalog_types", + "metric_optimization", + "conversion_tracking", + "data_provider_signals", + "max_optimization_goals", + "catalog_match", + "brief_relevance", + "expires_at", + "product_card", + "product_card_detailed" + ], + "type": "string" + }, + "minItems": 1, + "type": "array" }, "filters": { - "$ref": "/schemas/latest/core/product-filters.json" + "$ref": "../core/product-filters.json" }, "pagination": { - "$ref": "/schemas/latest/core/pagination-request.json" + "$ref": "../core/pagination-request.json" }, "property_list": { - "$ref": "/schemas/latest/core/property-list-ref.json", + "$ref": "../core/property-list-ref.json", "description": "[AdCP 3.0] Reference to an externally managed property list. When provided, the sales agent should filter products to only those available on properties in the list." + }, + "refine": { + "description": "Array of change requests for iterating on products and proposals from a previous get_products response. Each entry declares a scope (request, product, or proposal) and what the buyer is asking for. Only valid when buying_mode is 'refine'. The seller responds to each entry via refinement_applied in the response, matched by position.", + "items": { + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "ask": { + "description": "What the buyer is asking for at the request level (e.g., 'more video options and less display', 'suggest how to combine these products').", + "minLength": 1, + "type": "string" + }, + "scope": { + "const": "request", + "description": "Change scoped to the overall request \u2014 direction for the selection as a whole.", + "type": "string" + } + }, + "required": [ + "scope", + "ask" + ] + }, + { + "additionalProperties": false, + "properties": { + "action": { + "description": "'include': return this product with updated pricing and data. 'omit': exclude this product from the response. 'more_like_this': find additional products similar to this one (the original is also returned).", + "enum": [ + "include", + "omit", + "more_like_this" + ], + "type": "string" + }, + "ask": { + "description": "What the buyer is asking for on this product. For 'include': specific changes to request (e.g., 'add 16:9 format'). For 'more_like_this': what 'similar' means (e.g., 'same audience but video format'). Ignored when action is 'omit'.", + "minLength": 1, + "type": "string" + }, + "id": { + "description": "Product ID from a previous get_products response.", + "minLength": 1, + "type": "string" + }, + "scope": { + "const": "product", + "description": "Change scoped to a specific product.", + "type": "string" + } + }, + "required": [ + "scope", + "id", + "action" + ] + }, + { + "additionalProperties": false, + "properties": { + "action": { + "description": "'include': return this proposal with updated allocations and pricing. 'omit': exclude this proposal from the response.", + "enum": [ + "include", + "omit" + ], + "type": "string" + }, + "ask": { + "description": "What the buyer is asking for on this proposal (e.g., 'shift more budget toward video', 'reduce total by 10%'). Ignored when action is 'omit'.", + "minLength": 1, + "type": "string" + }, + "id": { + "description": "Proposal ID from a previous get_products response.", + "minLength": 1, + "type": "string" + }, + "scope": { + "const": "proposal", + "description": "Change scoped to a specific proposal.", + "type": "string" + } + }, + "required": [ + "scope", + "id", + "action" + ] + } + ], + "type": "object" + }, + "minItems": 1, + "type": "array" } }, - "required": [], + "required": [ + "buying_mode" + ], "title": "Get Products Request", "type": "object" } \ No newline at end of file diff --git a/schemas/cache/media-buy/get-products-response.json b/schemas/cache/media-buy/get-products-response.json index 159ea09b..e557349d 100644 --- a/schemas/cache/media-buy/get-products-response.json +++ b/schemas/cache/media-buy/get-products-response.json @@ -41,6 +41,45 @@ }, "type": "array" }, + "refinement_applied": { + "description": "Seller's response to each change request in the refine array, matched by position. Each entry acknowledges whether the corresponding ask was applied, partially applied, or unable to be fulfilled. MUST contain the same number of entries in the same order as the request's refine array. Only present when the request used buying_mode: 'refine'.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "description": "Echoes the id from the corresponding refine entry (for product and proposal scopes).", + "type": "string" + }, + "notes": { + "description": "Seller explanation of what was done, what couldn't be done, or why. Recommended when status is 'partial' or 'unable'.", + "type": "string" + }, + "scope": { + "description": "Echoes the scope from the corresponding refine entry. Allows orchestrators to cross-validate alignment.", + "enum": [ + "request", + "product", + "proposal" + ], + "type": "string" + }, + "status": { + "description": "'applied': the ask was fulfilled. 'partial': the ask was partially fulfilled \u2014 see notes for details. 'unable': the seller could not fulfill the ask \u2014 see notes for why.", + "enum": [ + "applied", + "partial", + "unable" + ], + "type": "string" + } + }, + "required": [ + "status" + ], + "type": "object" + }, + "type": "array" + }, "sandbox": { "description": "When true, this response contains simulated data from sandbox mode.", "type": "boolean" diff --git a/schemas/cache/media-buy/list-creative-formats-request.json b/schemas/cache/media-buy/list-creative-formats-request.json index 97cc12e8..7565417e 100644 --- a/schemas/cache/media-buy/list-creative-formats-request.json +++ b/schemas/cache/media-buy/list-creative-formats-request.json @@ -14,6 +14,15 @@ "context": { "$ref": "../core/context.json" }, + "disclosure_positions": { + "description": "Filter to formats whose supported_disclosure_positions include all of these positions. Use to find formats compatible with a brief's compliance requirements.", + "items": { + "$ref": "../enums/disclosure-position.json" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + }, "ext": { "$ref": "../core/ext.json" }, diff --git a/schemas/cache/media-buy/list-creatives-response.json b/schemas/cache/media-buy/list-creatives-response.json index 25273729..5fda8be6 100644 --- a/schemas/cache/media-buy/list-creatives-response.json +++ b/schemas/cache/media-buy/list-creatives-response.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/media-buy/list-creatives-response.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Response from creative library query with filtered results, metadata, and optional enriched data", @@ -10,6 +9,7 @@ { "assets": { "vast": { + "delivery_type": "url", "url": "https://vast.example.com/video/123", "vast_version": "4.1" } @@ -104,7 +104,7 @@ ], "properties": { "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "creatives": { "description": "Array of creative assets matching the query", @@ -112,43 +112,56 @@ "additionalProperties": true, "properties": { "account": { - "$ref": "/schemas/latest/core/account.json", + "$ref": "../core/account.json", "description": "Account that owns this creative" }, "assets": { - "description": "Assets for this creative, keyed by asset_role", + "additionalProperties": true, + "description": "Assets for this creative, keyed by asset_id", "patternProperties": { - "^[a-zA-Z0-9_-]+$": { - "oneOf": [ + "^[a-z0-9_]+$": { + "anyOf": [ + { + "$ref": "../core/assets/image-asset.json" + }, + { + "$ref": "../core/assets/video-asset.json" + }, + { + "$ref": "../core/assets/audio-asset.json" + }, { - "$ref": "/schemas/latest/core/assets/image-asset.json" + "$ref": "../core/assets/vast-asset.json" }, { - "$ref": "/schemas/latest/core/assets/video-asset.json" + "$ref": "../core/assets/text-asset.json" }, { - "$ref": "/schemas/latest/core/assets/audio-asset.json" + "$ref": "../core/assets/url-asset.json" }, { - "$ref": "/schemas/latest/core/assets/text-asset.json" + "$ref": "../core/assets/html-asset.json" }, { - "$ref": "/schemas/latest/core/assets/html-asset.json" + "$ref": "../core/assets/javascript-asset.json" }, { - "$ref": "/schemas/latest/core/assets/css-asset.json" + "$ref": "../core/assets/webhook-asset.json" }, { - "$ref": "/schemas/latest/core/assets/javascript-asset.json" + "$ref": "../core/assets/css-asset.json" }, { - "$ref": "/schemas/latest/core/assets/vast-asset.json" + "$ref": "../core/assets/daast-asset.json" }, { - "$ref": "/schemas/latest/core/assets/daast-asset.json" + "$ref": "../core/assets/markdown-asset.json" }, { - "$ref": "/schemas/latest/core/assets/url-asset.json" + "$ref": "../core/assets/brief-asset.json" + }, + { + "$ref": "../core/assets/catalog-asset.json" } ] } @@ -197,14 +210,6 @@ ], "type": "object" }, - "catalogs": { - "description": "Catalogs this creative renders, if any", - "items": { - "$ref": "/schemas/latest/core/catalog.json" - }, - "minItems": 1, - "type": "array" - }, "created_date": { "description": "When the creative was uploaded to the library", "format": "date-time", @@ -215,7 +220,7 @@ "type": "string" }, "format_id": { - "$ref": "/schemas/latest/core/format-id.json", + "$ref": "../core/format-id.json", "description": "Format identifier specifying which format this creative conforms to" }, "name": { @@ -266,13 +271,13 @@ "type": "object" }, "status": { - "$ref": "/schemas/latest/enums/creative-status.json", + "$ref": "../enums/creative-status.json", "description": "Current approval status of the creative" }, "sub_assets": { "description": "Sub-assets for multi-asset formats (included when include_sub_assets=true)", "items": { - "$ref": "/schemas/latest/core/sub-asset.json" + "$ref": "../core/sub-asset.json" }, "type": "array" }, @@ -302,7 +307,7 @@ "type": "array" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "format_summary": { "additionalProperties": true, @@ -317,7 +322,7 @@ "type": "object" }, "pagination": { - "$ref": "/schemas/latest/core/pagination-response.json" + "$ref": "../core/pagination-response.json" }, "query_summary": { "additionalProperties": true, @@ -339,7 +344,7 @@ "description": "Sort order that was applied", "properties": { "direction": { - "$ref": "/schemas/latest/enums/sort-direction.json" + "$ref": "../enums/sort-direction.json" }, "field": { "type": "string" diff --git a/schemas/cache/media-buy/package-request.json b/schemas/cache/media-buy/package-request.json index 6d792272..48f91f54 100644 --- a/schemas/cache/media-buy/package-request.json +++ b/schemas/cache/media-buy/package-request.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/media-buy/package-request.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Package configuration for media buy creation", @@ -18,14 +17,17 @@ "description": "Buyer's reference identifier for this package", "type": "string" }, - "catalog": { - "$ref": "/schemas/latest/core/catalog.json", - "description": "Catalog this package promotes. Makes the package catalog-driven: one budget envelope, platform optimizes across items. Reference a synced catalog by catalog_id with optional selectors to narrow scope." + "catalogs": { + "description": "Catalogs this package promotes. Each catalog MUST have a distinct type (e.g., one product catalog, one store catalog). This constraint is enforced at the application level \u2014 sellers MUST reject requests containing multiple catalogs of the same type with a validation_error. Makes the package catalog-driven: one budget envelope, platform optimizes across items.", + "items": { + "$ref": "../core/catalog.json" + }, + "type": "array" }, "creative_assignments": { "description": "Assign existing library creatives to this package with optional weights and placement targeting", "items": { - "$ref": "/schemas/latest/core/creative-assignment.json" + "$ref": "../core/creative-assignment.json" }, "minItems": 1, "type": "array" @@ -33,19 +35,19 @@ "creatives": { "description": "Upload new creative assets and assign to this package (creatives will be added to library). Use creative_assignments instead for existing library creatives.", "items": { - "$ref": "/schemas/latest/core/creative-asset.json" + "$ref": "../core/creative-asset.json" }, "maxItems": 100, "minItems": 1, "type": "array" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "format_ids": { "description": "Array of format IDs that will be used for this package - must be supported by the product. If omitted, defaults to all formats supported by the product.", "items": { - "$ref": "/schemas/latest/core/format-id.json" + "$ref": "../core/format-id.json" }, "minItems": 1, "type": "array" @@ -55,11 +57,16 @@ "minimum": 0, "type": "number" }, - "optimization_goal": { - "$ref": "/schemas/latest/core/optimization-goal.json" + "optimization_goals": { + "description": "Optimization targets for this package. The seller optimizes delivery toward these goals in priority order. Common pattern: event goals (purchase, install) as primary targets at priority 1; metric goals (clicks, views) as secondary proxy signals at priority 2+.", + "items": { + "$ref": "../core/optimization-goal.json" + }, + "minItems": 1, + "type": "array" }, "pacing": { - "$ref": "/schemas/latest/enums/pacing.json" + "$ref": "../enums/pacing.json" }, "paused": { "default": false, @@ -75,7 +82,7 @@ "type": "string" }, "targeting_overlay": { - "$ref": "/schemas/latest/core/targeting.json" + "$ref": "../core/targeting.json" } }, "required": [ diff --git a/schemas/cache/media-buy/package-update.json b/schemas/cache/media-buy/package-update.json index 462cebaf..f838da0a 100644 --- a/schemas/cache/media-buy/package-update.json +++ b/schemas/cache/media-buy/package-update.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/media-buy/package-update.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Package update configuration for update_media_buy. Identifies package by package_id or buyer_ref and specifies fields to modify. Fields not present are left unchanged. Note: product_id, format_ids, and pricing_option_id cannot be changed after creation.", @@ -30,39 +29,169 @@ "description": "Buyer's reference for the package to update", "type": "string" }, - "catalog": { - "$ref": "/schemas/latest/core/catalog.json", - "description": "Update the catalog this package promotes. Replaces the current catalog reference." + "catalogs": { + "description": "Replace the catalogs this package promotes. Uses replacement semantics \u2014 the provided array replaces the current list. Omit to leave catalogs unchanged.", + "items": { + "$ref": "../core/catalog.json" + }, + "minItems": 1, + "type": "array" }, "creative_assignments": { "description": "Replace creative assignments for this package with optional weights and placement targeting. Uses replacement semantics - omit to leave assignments unchanged.", "items": { - "$ref": "/schemas/latest/core/creative-assignment.json" + "$ref": "../core/creative-assignment.json" }, "type": "array" }, "creatives": { "description": "Upload new creative assets and assign to this package (creatives will be added to library). Use creative_assignments instead for existing library creatives.", "items": { - "$ref": "/schemas/latest/core/creative-asset.json" + "$ref": "../core/creative-asset.json" }, "maxItems": 100, "minItems": 1, "type": "array" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "impressions": { "description": "Updated impression goal for this package", "minimum": 0, "type": "number" }, - "optimization_goal": { - "$ref": "/schemas/latest/core/optimization-goal.json" + "keyword_targets_add": { + "description": "Keyword targets to add or update on this package. Upserts by (keyword, match_type) identity: if the pair already exists, its bid_price is updated; if not, a new keyword target is added. Use targeting_overlay.keyword_targets in create_media_buy to set the initial list.", + "items": { + "additionalProperties": false, + "properties": { + "bid_price": { + "description": "Per-keyword bid price. Inherits currency and max_bid interpretation from the package's pricing option.", + "minimum": 0, + "type": "number" + }, + "keyword": { + "description": "The keyword to target", + "minLength": 1, + "type": "string" + }, + "match_type": { + "description": "Match type for this keyword", + "enum": [ + "broad", + "phrase", + "exact" + ], + "type": "string" + } + }, + "required": [ + "keyword", + "match_type" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "keyword_targets_remove": { + "description": "Keyword targets to remove from this package. Removes matching (keyword, match_type) pairs. If a specified pair is not present, sellers SHOULD treat it as a no-op for that entry.", + "items": { + "additionalProperties": false, + "properties": { + "keyword": { + "description": "The keyword to stop targeting", + "minLength": 1, + "type": "string" + }, + "match_type": { + "description": "Match type to remove", + "enum": [ + "broad", + "phrase", + "exact" + ], + "type": "string" + } + }, + "required": [ + "keyword", + "match_type" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "negative_keywords_add": { + "description": "Negative keywords to add to this package. Appends to the existing negative keyword list \u2014 does not replace it. If a keyword+match_type pair already exists, sellers SHOULD treat it as a no-op for that entry. Use targeting_overlay.negative_keywords in create_media_buy to set the initial list.", + "items": { + "additionalProperties": false, + "properties": { + "keyword": { + "description": "The keyword to exclude", + "minLength": 1, + "type": "string" + }, + "match_type": { + "description": "Match type for exclusion", + "enum": [ + "broad", + "phrase", + "exact" + ], + "type": "string" + } + }, + "required": [ + "keyword", + "match_type" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "negative_keywords_remove": { + "description": "Negative keywords to remove from this package. Removes matching keyword+match_type pairs from the existing list. If a specified pair is not present, sellers SHOULD treat it as a no-op for that entry.", + "items": { + "additionalProperties": false, + "properties": { + "keyword": { + "description": "The keyword to stop excluding", + "minLength": 1, + "type": "string" + }, + "match_type": { + "description": "Match type to remove", + "enum": [ + "broad", + "phrase", + "exact" + ], + "type": "string" + } + }, + "required": [ + "keyword", + "match_type" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "optimization_goals": { + "description": "Replace all optimization goals for this package. Uses replacement semantics \u2014 omit to leave goals unchanged.", + "items": { + "$ref": "../core/optimization-goal.json" + }, + "minItems": 1, + "type": "array" }, "pacing": { - "$ref": "/schemas/latest/enums/pacing.json" + "$ref": "../enums/pacing.json" }, "package_id": { "description": "Publisher's ID of package to update", @@ -73,7 +202,8 @@ "type": "boolean" }, "targeting_overlay": { - "$ref": "/schemas/latest/core/targeting.json" + "$ref": "../core/targeting.json", + "description": "Targeting overlay to apply to this package. Uses replacement semantics \u2014 the full overlay replaces the previous one. Omit to leave targeting unchanged. For keyword and negative keyword updates, prefer the incremental operations (keyword_targets_add, keyword_targets_remove, negative_keywords_add, negative_keywords_remove) which avoid replacing the full overlay. Sellers SHOULD return a validation error if targeting_overlay.keyword_targets is present in the same request as keyword_targets_add or keyword_targets_remove, and likewise for negative_keywords." } }, "title": "Package Update", diff --git a/schemas/cache/media-buy/provide-performance-feedback-request.json b/schemas/cache/media-buy/provide-performance-feedback-request.json index 43e3f098..e7a8ae6a 100644 --- a/schemas/cache/media-buy/provide-performance-feedback-request.json +++ b/schemas/cache/media-buy/provide-performance-feedback-request.json @@ -41,25 +41,8 @@ "description": "Source of the performance data" }, "measurement_period": { - "additionalProperties": true, - "description": "Time period for performance measurement", - "properties": { - "end": { - "description": "ISO 8601 end timestamp for measurement period", - "format": "date-time", - "type": "string" - }, - "start": { - "description": "ISO 8601 start timestamp for measurement period", - "format": "date-time", - "type": "string" - } - }, - "required": [ - "start", - "end" - ], - "type": "object" + "$ref": "../core/datetime-range.json", + "description": "Time period for performance measurement" }, "media_buy_id": { "description": "Publisher's media buy identifier", diff --git a/schemas/cache/media-buy/sync-audiences-request.json b/schemas/cache/media-buy/sync-audiences-request.json index edc37579..26656e57 100644 --- a/schemas/cache/media-buy/sync-audiences-request.json +++ b/schemas/cache/media-buy/sync-audiences-request.json @@ -13,9 +13,9 @@ ] }, "properties": { - "account_id": { - "description": "Account to manage audiences for", - "type": "string" + "account": { + "$ref": "../core/account-ref.json", + "description": "Account to manage audiences for." }, "audiences": { "description": "Audiences to sync (create or update). When omitted, the call is discovery-only and returns all existing audiences on the account without modification.", @@ -34,20 +34,27 @@ "description": "Buyer's identifier for this audience. Used to reference the audience in targeting overlays.", "type": "string" }, - "consent_basis": { - "description": "GDPR lawful basis for processing this audience list. Informational \u2014 not validated by the protocol, but required by some sellers operating in regulated markets (e.g. EU). When omitted, the buyer asserts they have a lawful basis appropriate to their jurisdiction.", + "audience_type": { + "description": "Intended use for this audience. 'crm': target these users. 'suppression': exclude these users from delivery. 'lookalike_seed': use as a seed for the seller's lookalike modeling. Sellers may handle audiences differently based on type (e.g., suppression lists bypass minimum size requirements on some platforms).", "enum": [ - "consent", - "legitimate_interest", - "contract", - "legal_obligation" + "crm", + "suppression", + "lookalike_seed" ], "type": "string" }, + "consent_basis": { + "$ref": "../enums/consent-basis.json", + "description": "GDPR lawful basis for processing this audience list. Informational \u2014 not validated by the protocol, but required by some sellers operating in regulated markets (e.g. EU). When omitted, the buyer asserts they have a lawful basis appropriate to their jurisdiction." + }, "delete": { "description": "When true, delete this audience from the account entirely. All other fields on this audience object are ignored. Use this to delete a specific audience without affecting others.", "type": "boolean" }, + "description": { + "description": "Human-readable description of this audience's composition or purpose (e.g., 'High-value customers who purchased in the last 90 days').", + "type": "string" + }, "name": { "description": "Human-readable name for this audience", "type": "string" @@ -59,6 +66,15 @@ }, "minItems": 1, "type": "array" + }, + "tags": { + "description": "Buyer-defined tags for organizing and filtering audiences (e.g., 'holiday_2026', 'high_ltv'). Tags are stored by the seller and returned in discovery-only calls.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array", + "uniqueItems": true } }, "required": [ @@ -82,10 +98,9 @@ } }, "required": [ - "account_id" + "account" ], "then": { - "errorMessage": "audiences is required when delete_missing is true \u2014 omitting it would delete all buyer-managed audiences on the account", "required": [ "audiences" ] diff --git a/schemas/cache/media-buy/sync-audiences-response.json b/schemas/cache/media-buy/sync-audiences-response.json index f94df507..75d5f2f6 100644 --- a/schemas/cache/media-buy/sync-audiences-response.json +++ b/schemas/cache/media-buy/sync-audiences-response.json @@ -70,6 +70,11 @@ ], "type": "string" }, + "total_uploaded_count": { + "description": "Cumulative number of members uploaded across all syncs for this audience. Compare with matched_count to calculate match rate (matched_count / total_uploaded_count). Populated when the seller tracks cumulative upload counts.", + "minimum": 0, + "type": "integer" + }, "uploaded_count": { "description": "Number of members submitted in this sync operation (delta, not cumulative). In discovery-only calls (no audiences array), this is 0.", "minimum": 0, diff --git a/schemas/cache/media-buy/sync-catalogs-request.json b/schemas/cache/media-buy/sync-catalogs-request.json index 417aefff..cdf5dd58 100644 --- a/schemas/cache/media-buy/sync-catalogs-request.json +++ b/schemas/cache/media-buy/sync-catalogs-request.json @@ -5,7 +5,9 @@ "examples": [ { "data": { - "account_id": "acct_acmecorp", + "account": { + "account_id": "acct_acmecorp" + }, "catalogs": [ { "catalog_id": "product-feed", @@ -29,7 +31,9 @@ }, { "data": { - "account_id": "acct_restaurants", + "account": { + "account_id": "acct_restaurants" + }, "catalogs": [ { "catalog_id": "chef-vacancies", @@ -58,7 +62,9 @@ }, { "data": { - "account_id": "acct_acmecorp" + "account": { + "account_id": "acct_acmecorp" + } }, "description": "Discovery-only: list all catalogs on the account" } @@ -74,9 +80,9 @@ ] }, "properties": { - "account_id": { - "description": "Account that owns these catalogs. Required if the agent has multiple accounts and the seller cannot route automatically.", - "type": "string" + "account": { + "$ref": "../core/account-ref.json", + "description": "Account that owns these catalogs." }, "catalog_ids": { "description": "Optional filter to limit sync scope to specific catalog IDs. When provided, only these catalogs will be created/updated. Other catalogs on the account are unaffected.", @@ -122,6 +128,9 @@ "description": "Validation strictness. 'strict' fails entire sync on any validation error. 'lenient' processes valid catalogs and reports errors." } }, + "required": [ + "account" + ], "then": { "errorMessage": "catalogs is required when delete_missing is true \u2014 omitting it would delete all buyer-managed catalogs on the account", "required": [ diff --git a/schemas/cache/media-buy/sync-creatives-request.json b/schemas/cache/media-buy/sync-creatives-request.json index fcc07c50..ec50f492 100644 --- a/schemas/cache/media-buy/sync-creatives-request.json +++ b/schemas/cache/media-buy/sync-creatives-request.json @@ -1,17 +1,23 @@ { - "$id": "/schemas/latest/media-buy/sync-creatives-request.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Request parameters for syncing creative assets with upsert semantics - supports bulk operations, scoped updates, and assignment management", "examples": [ { "data": { - "assignments": { - "hero_video_30s": [ - "pkg_ctv_001", - "pkg_ctv_002" - ] + "account": { + "account_id": "acct_acmecorp" }, + "assignments": [ + { + "creative_id": "hero_video_30s", + "package_id": "pkg_ctv_001" + }, + { + "creative_id": "hero_video_30s", + "package_id": "pkg_ctv_002" + } + ], "creatives": [ { "assets": { @@ -39,19 +45,20 @@ }, { "data": { + "account": { + "account_id": "acct_acmecorp" + }, "creatives": [ { "assets": { "generation_prompt": { "content": "Create a warm, festive holiday campaign featuring winter products" - } - }, - "catalogs": [ - { + }, + "product_catalog": { "catalog_id": "winter-products", "type": "product" } - ], + }, "creative_id": "holiday_hero", "format_id": { "agent_url": "https://publisher.com/.well-known/adcp/sales", @@ -69,6 +76,9 @@ }, { "data": { + "account": { + "account_id": "acct_acmecorp" + }, "creative_ids": [ "hero_video_30s", "banner_300x250" @@ -111,26 +121,49 @@ } ], "properties": { - "account_id": { - "description": "Account that owns these creatives. Optional if the agent has a single account or the seller can determine the account from context. Required if the agent has multiple accounts and the seller cannot route automatically.", - "type": "string" + "account": { + "$ref": "../core/account-ref.json", + "description": "Account that owns these creatives." }, "assignments": { - "additionalProperties": true, - "description": "Optional bulk assignment of creatives to packages", - "patternProperties": { - "^[a-zA-Z0-9_-]+$": { - "description": "Array of package IDs to assign this creative to", - "items": { + "description": "Optional bulk assignment of creatives to packages. Each entry maps one creative to one package with optional weight and placement targeting.", + "items": { + "additionalProperties": false, + "properties": { + "creative_id": { + "description": "ID of the creative to assign", + "type": "string" + }, + "package_id": { + "description": "ID of the package to assign the creative to", "type": "string" }, - "type": "array" - } + "placement_ids": { + "description": "Restrict this creative to specific placements within the package. When omitted, the creative is eligible for all placements.", + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array" + }, + "weight": { + "description": "Relative delivery weight (0\u2013100). When multiple creatives are assigned to the same package, weights determine impression distribution proportionally \u2014 a creative with weight 2 gets twice the delivery of weight 1. When omitted, the creative receives equal rotation with other unweighted creatives. A weight of 0 means the creative is assigned but paused (receives no delivery).", + "maximum": 100, + "minimum": 0, + "type": "number" + } + }, + "required": [ + "creative_id", + "package_id" + ], + "type": "object" }, - "type": "object" + "minItems": 1, + "type": "array" }, "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "creative_ids": { "description": "Optional filter to limit sync scope to specific creative IDs. When provided, only these creatives will be created/updated. Other creatives in the library are unaffected. Useful for partial updates and error recovery.", @@ -144,7 +177,7 @@ "creatives": { "description": "Array of creative assets to sync (create or update)", "items": { - "$ref": "/schemas/latest/core/creative-asset.json" + "$ref": "../core/creative-asset.json" }, "maxItems": 100, "minItems": 1, @@ -161,19 +194,26 @@ "type": "boolean" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" + }, + "idempotency_key": { + "description": "Client-generated idempotency key for safe retries. If a sync fails without a response, resending with the same idempotency_key guarantees at-most-once execution.", + "maxLength": 255, + "minLength": 8, + "type": "string" }, "push_notification_config": { - "$ref": "/schemas/latest/core/push-notification-config.json", + "$ref": "../core/push-notification-config.json", "description": "Optional webhook configuration for async sync notifications. Publisher will send webhook when sync completes if operation takes longer than immediate response time (typically for large bulk operations or manual approval/HITL)." }, "validation_mode": { - "$ref": "/schemas/latest/enums/validation-mode.json", + "$ref": "../enums/validation-mode.json", "default": "strict", "description": "Validation strictness. 'strict' fails entire sync on any validation error. 'lenient' processes valid creatives and reports errors." } }, "required": [ + "account", "creatives" ], "title": "Sync Creatives Request", diff --git a/schemas/cache/media-buy/sync-event-sources-request.json b/schemas/cache/media-buy/sync-event-sources-request.json index 38f88411..938a010b 100644 --- a/schemas/cache/media-buy/sync-event-sources-request.json +++ b/schemas/cache/media-buy/sync-event-sources-request.json @@ -3,9 +3,9 @@ "additionalProperties": true, "description": "Request parameters for configuring event sources on an account with upsert semantics. Existing event sources matched by event_source_id are updated, new ones are created. When delete_missing is true, buyer-managed event sources on the account not in this request are removed. When event_sources is omitted, the call is discovery-only: it returns all event sources on the account without modification. The response always includes both synced and seller-managed event sources for full visibility.", "properties": { - "account_id": { - "description": "Account to configure event sources for", - "type": "string" + "account": { + "$ref": "../core/account-ref.json", + "description": "Account to configure event sources for." }, "context": { "$ref": "../core/context.json" @@ -58,7 +58,7 @@ } }, "required": [ - "account_id" + "account" ], "title": "Sync Event Sources Request", "type": "object" diff --git a/schemas/cache/media-buy/update-media-buy-request.json b/schemas/cache/media-buy/update-media-buy-request.json index 07bc0d1a..cba5641d 100644 --- a/schemas/cache/media-buy/update-media-buy-request.json +++ b/schemas/cache/media-buy/update-media-buy-request.json @@ -30,6 +30,12 @@ "ext": { "$ref": "../core/ext.json" }, + "idempotency_key": { + "description": "Client-generated idempotency key for safe retries. If an update fails without a response, resending with the same idempotency_key guarantees the update is applied at most once.", + "maxLength": 255, + "minLength": 8, + "type": "string" + }, "media_buy_id": { "description": "Publisher's ID of the media buy to update", "type": "string" diff --git a/schemas/cache/pricing-options/cpc-option.json b/schemas/cache/pricing-options/cpc-option.json index ed149034..bcefc48e 100644 --- a/schemas/cache/pricing-options/cpc-option.json +++ b/schemas/cache/pricing-options/cpc-option.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/pricing-options/cpc-option.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Cost Per Click pricing. If fixed_price is present, it's fixed pricing. If absent, it's auction-based.", @@ -36,7 +35,7 @@ "type": "number" }, "price_guidance": { - "$ref": "/schemas/latest/pricing-options/price-guidance.json", + "$ref": "price-guidance.json", "description": "Optional pricing guidance for auction-based bidding" }, "pricing_model": { diff --git a/schemas/cache/pricing-options/cpcv-option.json b/schemas/cache/pricing-options/cpcv-option.json index 0445f1a7..46910670 100644 --- a/schemas/cache/pricing-options/cpcv-option.json +++ b/schemas/cache/pricing-options/cpcv-option.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/pricing-options/cpcv-option.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Cost Per Completed View (100% video/audio completion) pricing. If fixed_price is present, it's fixed pricing. If absent, it's auction-based.", @@ -36,7 +35,7 @@ "type": "number" }, "price_guidance": { - "$ref": "/schemas/latest/pricing-options/price-guidance.json", + "$ref": "price-guidance.json", "description": "Optional pricing guidance for auction-based bidding" }, "pricing_model": { diff --git a/schemas/cache/pricing-options/cpm-option.json b/schemas/cache/pricing-options/cpm-option.json index 2135d6da..2b449ef3 100644 --- a/schemas/cache/pricing-options/cpm-option.json +++ b/schemas/cache/pricing-options/cpm-option.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/pricing-options/cpm-option.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Cost Per Mille (cost per 1,000 impressions) pricing. If fixed_price is present, it's fixed pricing. If absent, it's auction-based.", @@ -36,7 +35,7 @@ "type": "number" }, "price_guidance": { - "$ref": "/schemas/latest/pricing-options/price-guidance.json", + "$ref": "price-guidance.json", "description": "Optional pricing guidance for auction-based bidding" }, "pricing_model": { diff --git a/schemas/cache/pricing-options/cpv-option.json b/schemas/cache/pricing-options/cpv-option.json index 10b2bb70..fc10aa43 100644 --- a/schemas/cache/pricing-options/cpv-option.json +++ b/schemas/cache/pricing-options/cpv-option.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/pricing-options/cpv-option.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Cost Per View (at publisher-defined threshold) pricing for video/audio. If fixed_price is present, it's fixed pricing. If absent, it's auction-based.", @@ -71,7 +70,7 @@ "type": "object" }, "price_guidance": { - "$ref": "/schemas/latest/pricing-options/price-guidance.json", + "$ref": "price-guidance.json", "description": "Optional pricing guidance for auction-based bidding" }, "pricing_model": { diff --git a/schemas/cache/pricing-options/vcpm-option.json b/schemas/cache/pricing-options/vcpm-option.json index 0481dbd4..19d46974 100644 --- a/schemas/cache/pricing-options/vcpm-option.json +++ b/schemas/cache/pricing-options/vcpm-option.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/pricing-options/vcpm-option.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Viewable Cost Per Mille (cost per 1,000 viewable impressions) pricing - MRC viewability standard. If fixed_price is present, it's fixed pricing. If absent, it's auction-based.", @@ -36,7 +35,7 @@ "type": "number" }, "price_guidance": { - "$ref": "/schemas/latest/pricing-options/price-guidance.json", + "$ref": "price-guidance.json", "description": "Optional pricing guidance for auction-based bidding" }, "pricing_model": { diff --git a/schemas/cache/property/property-list-filters.json b/schemas/cache/property/property-list-filters.json index fde6cd21..520af264 100644 --- a/schemas/cache/property/property-list-filters.json +++ b/schemas/cache/property/property-list-filters.json @@ -4,7 +4,7 @@ "description": "Filters that dynamically modify a property list when resolved", "properties": { "channels_any": { - "description": "Property must support ANY of the listed channels. Required.", + "description": "Property must support ANY of the listed channels. When omitted, no channel restriction is applied.", "items": { "$ref": "../enums/channels.json" }, @@ -12,7 +12,7 @@ "type": "array" }, "countries_all": { - "description": "Property must have feature data for ALL listed countries (ISO codes). Required.", + "description": "Property must have feature data for ALL listed countries (ISO codes). When omitted, no country restriction is applied.", "items": { "pattern": "^[A-Z]{2}$", "type": "string" @@ -45,10 +45,6 @@ "type": "array" } }, - "required": [ - "countries_all", - "channels_any" - ], "title": "Property List Filters", "type": "object" } \ No newline at end of file diff --git a/schemas/cache/protocol/get-adcp-capabilities-response.json b/schemas/cache/protocol/get-adcp-capabilities-response.json index e8a6116d..dc82d009 100644 --- a/schemas/cache/protocol/get-adcp-capabilities-response.json +++ b/schemas/cache/protocol/get-adcp-capabilities-response.json @@ -1,5 +1,4 @@ { - "$id": "/schemas/latest/protocol/get-adcp-capabilities-response.json", "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, "description": "Response payload for get_adcp_capabilities task. Protocol-level capability discovery across all AdCP protocols. Each domain protocol has its own capability section.", @@ -7,20 +6,25 @@ "account": { "description": "Account management capabilities. Describes how accounts are established, what billing models are supported, and whether an account is required before browsing products.", "properties": { - "authorization_endpoint": { - "description": "OAuth authorization endpoint for obtaining operator-level credentials. Present when the seller supports OAuth for operator authentication. The agent directs the operator to this URL to authenticate and obtain a bearer token. If absent and require_operator_auth is true, operators obtain credentials out-of-band (e.g., seller portal, API key).", - "format": "uri", - "type": "string" + "account_financials": { + "default": false, + "description": "Whether this seller supports the get_account_financials task for querying account-level financial status (spend, credit, invoices). Only applicable to operator-billed accounts.", + "type": "boolean" }, - "default_billing": { - "description": "The billing model applied when the agent omits billing from a sync_accounts request. Must be one of the values in supported_billing.", + "account_resolution": { + "default": "explicit_account_id", + "description": "How the seller resolves account references. explicit_account_id: accounts are managed out-of-band (advertiser portal, sales rep) and discovered via list_accounts. implicit_from_sync: buyer declares intent via sync_accounts and the seller provisions accounts.", "enum": [ - "brand", - "operator", - "agent" + "explicit_account_id", + "implicit_from_sync" ], "type": "string" }, + "authorization_endpoint": { + "description": "OAuth authorization endpoint for obtaining operator-level credentials. Present when the seller supports OAuth for operator authentication. The agent directs the operator to this URL to authenticate and obtain a bearer token. If absent and require_operator_auth is true, operators obtain credentials out-of-band (e.g., seller portal, API key).", + "format": "uri", + "type": "string" + }, "require_operator_auth": { "default": false, "description": "Whether the seller requires operator-level credentials. When false (default), the seller trusts the agent's identity claims \u2014 the agent authenticates once and declares brands/operators via sync_accounts. When true, each operator must authenticate independently with the seller, and the agent opens a per-operator session using the operator's credential.", @@ -28,14 +32,13 @@ }, "required_for_products": { "default": false, - "description": "Whether an active account is required to call get_products. When true, the agent must establish an account via sync_accounts before browsing products. When false, get_products works without an account (account_id is optional for rate-card-specific pricing).", + "description": "Whether an account reference is required for get_products. When true, the buyer must establish an account before browsing products. When false (default), the buyer can browse products without an account \u2014 useful for price comparison and discovery before committing to a seller.", "type": "boolean" }, "supported_billing": { - "description": "Billing models this seller supports. brand: seller invoices the brand directly. operator: seller invoices the operator (agency). agent: agent consolidates billing.", + "description": "Billing models this seller supports. operator: seller invoices the operator (agency or brand buying direct). agent: agent consolidates billing. The buyer must pass one of these values in sync_accounts.", "items": { "enum": [ - "brand", "operator", "agent" ], @@ -54,7 +57,7 @@ "description": "Core AdCP protocol information", "properties": { "major_versions": { - "description": "AdCP major versions supported by this seller. Major versions indicate breaking changes.", + "description": "AdCP major versions supported by this seller. Major versions indicate breaking changes. When multiple versions are listed, the buyer declares its version during the capabilities handshake or via the adcp_version field on requests.", "items": { "minimum": 1, "type": "integer" @@ -69,14 +72,14 @@ "type": "object" }, "context": { - "$ref": "/schemas/latest/core/context.json" + "$ref": "../core/context.json" }, "creative": { "additionalProperties": true, "description": "Creative protocol capabilities. Only present if creative is in supported_protocols.", "properties": { - "supports_brief": { - "description": "Whether this creative agent accepts creative_brief in build_creative requests for structured campaign-level creative direction", + "supports_compliance": { + "description": "When true, this creative agent can process briefs with compliance requirements (required_disclosures, prohibited_claims) and will validate that disclosures can be satisfied by the target format.", "type": "boolean" } }, @@ -85,12 +88,12 @@ "errors": { "description": "Task-specific errors and warnings", "items": { - "$ref": "/schemas/latest/core/error.json" + "$ref": "../core/error.json" }, "type": "array" }, "ext": { - "$ref": "/schemas/latest/core/ext.json" + "$ref": "../core/ext.json" }, "extensions_supported": { "description": "Extension namespaces this agent supports. Buyers can expect meaningful data in ext.{namespace} fields on responses from this agent. Extension schemas are published in the AdCP extension registry.", @@ -261,7 +264,7 @@ "type": "integer" }, "supported_identifier_types": { - "description": "Hashed PII types accepted for audience matching. Buyers should only send identifiers the seller supports.", + "description": "PII-derived identifier types accepted for audience matching. Buyers should only send identifiers the seller supports.", "items": { "enum": [ "hashed_email", @@ -275,10 +278,14 @@ "supported_uid_types": { "description": "Universal ID types accepted for audience matching (MAIDs, RampID, UID2, etc.). MAID support varies significantly by platform \u2014 check this field before sending uids with type: maid.", "items": { - "$ref": "/schemas/latest/enums/uid-type.json" + "$ref": "../enums/uid-type.json" }, "minItems": 1, "type": "array" + }, + "supports_platform_customer_id": { + "description": "Whether the seller accepts the buyer's CRM/loyalty ID as a matchable identifier. Only applicable when the seller operates a closed ecosystem with a shared ID namespace (e.g., a retailer matching against their loyalty program). When true, buyers can include platform_customer_id values in AudienceMember.identifiers for matching against the seller's identity graph. Reporting on matched platform_customer_ids typically requires a clean room or the seller's own reporting surface.", + "type": "boolean" } }, "required": [ @@ -292,42 +299,46 @@ "description": "Seller-level conversion tracking capabilities. Only present when features.conversion_tracking is true.", "properties": { "attribution_windows": { - "description": "Attribution windows available from this seller. Single-element arrays indicate fixed windows; multi-element arrays indicate configurable options the buyer can choose from via optimization_goal.attribution_window on packages.", + "description": "Attribution windows available from this seller. Single-element arrays indicate fixed windows; multi-element arrays indicate configurable options the buyer can choose from via attribution_window on optimization goals.", "items": { "additionalProperties": true, "properties": { - "click_through": { - "description": "Available click-through attribution windows (e.g. [\"7d\"], [\"7d\", \"14d\", \"30d\"])", + "event_type": { + "$ref": "../enums/event-type.json", + "description": "Event type this window applies to, or omit for default window" + }, + "post_click": { + "description": "Available post-click attribution windows (e.g. [{\"interval\": 7, \"unit\": \"days\"}])", "items": { - "type": "string" + "$ref": "../core/duration.json" }, "minItems": 1, "type": "array" }, - "event_type": { - "$ref": "/schemas/latest/enums/event-type.json", - "description": "Event type this window applies to, or omit for default window" - }, - "view_through": { - "description": "Available view-through attribution windows (e.g. [\"1d\"], [\"1d\", \"7d\", \"14d\"])", + "post_view": { + "description": "Available post-view attribution windows (e.g. [{\"interval\": 1, \"unit\": \"days\"}])", "items": { - "type": "string" + "$ref": "../core/duration.json" }, "minItems": 1, "type": "array" } }, "required": [ - "click_through" + "post_click" ], "type": "object" }, "type": "array" }, + "multi_source_event_dedup": { + "description": "Whether this seller can deduplicate conversion events across multiple event sources within a single goal. When true, the seller honors the deduplication semantics in optimization_goals event_sources arrays \u2014 the same event_id from multiple sources counts once. When false or absent, buyers should use a single event source per goal; multi-source arrays will be treated as first-source-wins. Most social platforms cannot deduplicate across independently-managed pixel and CAPI sources.", + "type": "boolean" + }, "supported_action_sources": { "description": "Action sources this seller accepts events from", "items": { - "$ref": "/schemas/latest/enums/action-source.json" + "$ref": "../enums/action-source.json" }, "minItems": 1, "type": "array" @@ -335,7 +346,7 @@ "supported_event_types": { "description": "Event types this seller can track and attribute. If omitted, all standard event types are supported.", "items": { - "$ref": "/schemas/latest/enums/event-type.json" + "$ref": "../enums/event-type.json" }, "minItems": 1, "type": "array" @@ -355,7 +366,7 @@ "supported_uid_types": { "description": "Universal ID types accepted for user matching", "items": { - "$ref": "/schemas/latest/enums/uid-type.json" + "$ref": "../enums/uid-type.json" }, "minItems": 1, "type": "array" @@ -417,7 +428,7 @@ "verification_methods": { "description": "Age verification methods this seller supports", "items": { - "$ref": "/schemas/latest/enums/age-verification-method.json" + "$ref": "../enums/age-verification-method.json" }, "type": "array" } @@ -436,6 +447,10 @@ "description": "Whether seller supports device platform targeting (Sec-CH-UA-Platform values)", "type": "boolean" }, + "device_type": { + "description": "Whether seller supports device type targeting (form factor: desktop, mobile, tablet, ctv, dooh, unknown). When true, seller supports both device_type (include) and device_type_exclude (exclude) in targeting overlays.", + "type": "boolean" + }, "geo_countries": { "description": "Supports country-level geo targeting using ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE')", "type": "boolean" @@ -465,6 +480,10 @@ "geo_postal_areas": { "description": "Postal area targeting support. Specifies which postal code systems are supported. System names encode country and precision.", "properties": { + "at_plz": { + "description": "Austrian Postleitzahl, 4 digits (e.g., '1010')", + "type": "boolean" + }, "au_postcode": { "description": "Australian postcode, 4 digits (e.g., '2000')", "type": "boolean" @@ -477,6 +496,10 @@ "description": "Canadian full postal code (e.g., 'K1A 0B1')", "type": "boolean" }, + "ch_plz": { + "description": "Swiss Postleitzahl, 4 digits (e.g., '8000')", + "type": "boolean" + }, "de_plz": { "description": "German Postleitzahl, 5 digits (e.g., '10115')", "type": "boolean" @@ -504,13 +527,83 @@ }, "type": "object" }, + "geo_proximity": { + "description": "Proximity targeting capabilities from arbitrary coordinates via targeting_overlay.geo_proximity.", + "properties": { + "geometry": { + "description": "Whether seller supports pre-computed GeoJSON geometry (buyer provides the polygon)", + "type": "boolean" + }, + "radius": { + "description": "Whether seller supports simple radius targeting (distance circle from a point)", + "type": "boolean" + }, + "transport_modes": { + "description": "Transport modes supported for travel_time isochrones. Only relevant when travel_time is true.", + "items": { + "$ref": "../enums/transport-mode.json" + }, + "minItems": 1, + "type": "array" + }, + "travel_time": { + "description": "Whether seller supports travel time isochrone targeting (requires a routing engine)", + "type": "boolean" + } + }, + "type": "object" + }, "geo_regions": { "description": "Supports region/state-level geo targeting using ISO 3166-2 subdivision codes (e.g., 'US-NY', 'GB-SCT', 'DE-BY')", "type": "boolean" }, + "keyword_targets": { + "description": "Keyword targeting capabilities. Presence indicates support for targeting_overlay.keyword_targets and keyword_targets_add/remove in update_media_buy.", + "properties": { + "supported_match_types": { + "description": "Match types this seller supports for keyword targets. Sellers must reject goals with unsupported match types.", + "items": { + "enum": [ + "broad", + "phrase", + "exact" + ], + "type": "string" + }, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "supported_match_types" + ], + "type": "object" + }, "language": { "description": "Whether seller supports language targeting (ISO 639-1 codes)", "type": "boolean" + }, + "negative_keywords": { + "description": "Negative keyword capabilities. Presence indicates support for targeting_overlay.negative_keywords and negative_keywords_add/remove in update_media_buy.", + "properties": { + "supported_match_types": { + "description": "Match types this seller supports for negative keywords. Sellers must reject goals with unsupported match types.", + "items": { + "enum": [ + "broad", + "phrase", + "exact" + ], + "type": "string" + }, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "supported_match_types" + ], + "type": "object" } }, "type": "object" @@ -519,7 +612,7 @@ "type": "object" }, "features": { - "$ref": "/schemas/latest/core/media-buy-features.json" + "$ref": "../core/media-buy-features.json" }, "portfolio": { "description": "Information about the seller's media inventory portfolio", @@ -537,7 +630,7 @@ "primary_channels": { "description": "Primary advertising channels in this portfolio", "items": { - "$ref": "/schemas/latest/enums/channels.json" + "$ref": "../enums/channels.json" }, "type": "array" }, @@ -563,6 +656,51 @@ "publisher_domains" ], "type": "object" + }, + "reporting": { + "description": "Seller-level reporting capabilities. Summarizes what reporting features are available across the seller's product portfolio. Individual products may vary \u2014 check product-level reporting_capabilities for specifics.", + "properties": { + "available_dimensions": { + "description": "Reporting dimensions available across the seller's portfolio. Individual products may support a subset. Dimensions geo, device_type, device_platform, audience, and placement are opt-in via reporting_dimensions on the delivery request. Dimensions creative, keyword, and catalog_item are included automatically when the seller supports them (not controlled by reporting_dimensions).", + "items": { + "enum": [ + "geo", + "device_type", + "device_platform", + "audience", + "placement", + "creative", + "keyword", + "catalog_item" + ], + "type": "string" + }, + "type": "array", + "uniqueItems": true + }, + "supports_daily_breakdown": { + "description": "Whether delivery reporting includes daily_breakdown at the media buy and/or package level.", + "type": "boolean" + }, + "supports_date_range": { + "description": "Whether any products support date range filtering (date_range_support: 'date_range'). When false, all products return lifetime-only data.", + "type": "boolean" + }, + "supports_webhooks": { + "description": "Whether any products support webhook-based reporting notifications.", + "type": "boolean" + } + }, + "type": "object" + }, + "supported_pricing_models": { + "description": "Pricing models this seller supports across its product portfolio. Buyers can use this for pre-flight filtering before querying individual products. Individual products may support a subset of these models.", + "items": { + "$ref": "../enums/pricing-model.json" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true } }, "type": "object" @@ -604,7 +742,7 @@ "type": "string" }, "capabilities": { - "$ref": "/schemas/latest/sponsored-intelligence/si-capabilities.json", + "$ref": "../sponsored-intelligence/si-capabilities.json", "description": "Modalities, components, and commerce capabilities" }, "endpoint": { diff --git a/schemas/cache/signals/activate-signal-request.json b/schemas/cache/signals/activate-signal-request.json index 49d89f43..7d1c7701 100644 --- a/schemas/cache/signals/activate-signal-request.json +++ b/schemas/cache/signals/activate-signal-request.json @@ -1,13 +1,30 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": true, - "description": "Request parameters for activating a signal on a specific deployment target", + "description": "Request parameters for activating or deactivating a signal on deployment targets", "properties": { + "account": { + "$ref": "../core/account-ref.json", + "description": "Account for this activation. Associates with a commercial relationship established via sync_accounts." + }, + "action": { + "default": "activate", + "description": "Whether to activate or deactivate the signal. Deactivating removes the segment from downstream platforms, required when campaigns end to comply with data governance policies (GDPR, CCPA). Defaults to 'activate' when omitted.", + "enum": [ + "activate", + "deactivate" + ], + "type": "string" + }, + "buyer_campaign_ref": { + "description": "The buyer's campaign reference for this activation. Enables the signals agent to correlate activations with subsequent report_usage calls.", + "type": "string" + }, "context": { "$ref": "../core/context.json" }, - "deployments": { - "description": "Target deployment(s) for activation. If the authenticated caller matches one of these deployment targets, activation keys will be included in the response.", + "destinations": { + "description": "Target destination(s) for activation. If the authenticated caller matches one of these destinations, activation keys will be included in the response.", "items": { "$ref": "../core/destination.json" }, @@ -17,6 +34,10 @@ "ext": { "$ref": "../core/ext.json" }, + "pricing_option_id": { + "description": "The pricing option selected from the signal's pricing_options in the get_signals response. Required when the signal has pricing options. Records the buyer's pricing commitment at activation time; pass this same value in report_usage for billing verification.", + "type": "string" + }, "signal_agent_segment_id": { "description": "The universal identifier for the signal to activate", "type": "string" @@ -24,7 +45,7 @@ }, "required": [ "signal_agent_segment_id", - "deployments" + "destinations" ], "title": "Activate Signal Request", "type": "object" diff --git a/schemas/cache/signals/get-signals-request.json b/schemas/cache/signals/get-signals-request.json index a2dc51dc..9e46a3cb 100644 --- a/schemas/cache/signals/get-signals-request.json +++ b/schemas/cache/signals/get-signals-request.json @@ -13,38 +13,35 @@ ] } ], - "description": "Request parameters for discovering signals. Use signal_spec for natural language discovery, signal_ids for exact lookups, or both (signal_ids take precedence for exact matches, signal_spec provides additional discovery context).", + "description": "Request parameters for discovering and refining signals. Use signal_spec for natural language discovery, signal_ids for exact lookups, or both to refine previous results (signal_ids anchor the starting set, signal_spec guides adjustments).", "properties": { + "account": { + "$ref": "../core/account-ref.json", + "description": "Account for this request. When provided, the signals agent returns per-account pricing options if configured." + }, + "buyer_campaign_ref": { + "description": "The buyer's campaign reference. Used to correlate signal discovery with subsequent report_usage calls.", + "type": "string" + }, "context": { "$ref": "../core/context.json" }, - "deliver_to": { - "additionalProperties": true, - "description": "Deployment targets where signals need to be activated", - "properties": { - "countries": { - "description": "Countries where signals will be used (ISO codes)", - "items": { - "pattern": "^[A-Z]{2}$", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "deployments": { - "description": "List of deployment targets (DSPs, sales agents, etc.). If the authenticated caller matches one of these deployment targets, activation keys will be included in the response.", - "items": { - "$ref": "../core/destination.json" - }, - "minItems": 1, - "type": "array" - } + "countries": { + "description": "Countries where signals will be used (ISO 3166-1 alpha-2 codes). When omitted, no geographic filter is applied.", + "items": { + "pattern": "^[A-Z]{2}$", + "type": "string" }, - "required": [ - "deployments", - "countries" - ], - "type": "object" + "minItems": 1, + "type": "array" + }, + "destinations": { + "description": "Filter signals to those activatable on specific agents/platforms. When omitted, returns all signals available on the current agent. If the authenticated caller matches one of these destinations, activation keys will be included in the response.", + "items": { + "$ref": "../core/destination.json" + }, + "minItems": 1, + "type": "array" }, "ext": { "$ref": "../core/ext.json" @@ -61,7 +58,7 @@ "$ref": "../core/pagination-request.json" }, "signal_ids": { - "description": "Specific signals to look up by data provider and ID. Returns exact matches from the data provider's catalog. Takes precedence over signal_spec when both are provided.", + "description": "Specific signals to look up by data provider and ID. Returns exact matches from the data provider's catalog. When combined with signal_spec, these signals anchor the starting set and signal_spec guides adjustments.", "items": { "$ref": "../core/signal-id.json" }, @@ -73,9 +70,6 @@ "type": "string" } }, - "required": [ - "deliver_to" - ], "title": "Get Signals Request", "type": "object" } \ No newline at end of file diff --git a/schemas/cache/signals/get-signals-response.json b/schemas/cache/signals/get-signals-response.json index 2f6e5c0b..72b0e0ef 100644 --- a/schemas/cache/signals/get-signals-response.json +++ b/schemas/cache/signals/get-signals-response.json @@ -28,6 +28,13 @@ "items": { "additionalProperties": true, "properties": { + "categories": { + "description": "Valid values for categorical signals. Present when value_type is 'categorical'. Buyers must use one of these values in SignalTargeting.values.", + "items": { + "type": "string" + }, + "type": "array" + }, "coverage_percentage": { "description": "Percentage of audience coverage", "maximum": 100, @@ -53,24 +60,30 @@ "description": "Human-readable signal name", "type": "string" }, - "pricing": { - "additionalProperties": true, - "description": "Pricing information", + "pricing_options": { + "description": "Pricing options available for this signal. The buyer selects one and passes its pricing_option_id in report_usage for billing verification.", + "items": { + "$ref": "../core/signal-pricing-option.json" + }, + "minItems": 1, + "type": "array" + }, + "range": { + "additionalProperties": false, + "description": "Valid range for numeric signals. Present when value_type is 'numeric'.", "properties": { - "cpm": { - "description": "Cost per thousand impressions", - "minimum": 0, + "max": { + "description": "Maximum value (inclusive)", "type": "number" }, - "currency": { - "description": "Currency code", - "pattern": "^[A-Z]{3}$", - "type": "string" + "min": { + "description": "Minimum value (inclusive)", + "type": "number" } }, "required": [ - "cpm", - "currency" + "min", + "max" ], "type": "object" }, @@ -99,7 +112,7 @@ "data_provider", "coverage_percentage", "deployments", - "pricing" + "pricing_options" ], "type": "object" }, diff --git a/scripts/consolidate_exports.py b/scripts/consolidate_exports.py index bce142ec..e97cef55 100644 --- a/scripts/consolidate_exports.py +++ b/scripts/consolidate_exports.py @@ -78,6 +78,13 @@ def _module_sort_key(p: Path) -> tuple[int, str]: # Note: "Catalog" also collides between core.catalog and media_buy.sync_catalogs_response. # We intentionally let core.catalog win (first-seen, since core/ sorts before media_buy/). # The response-level Catalog is imported directly in aliases.py as SyncCatalogResult. + # Audience collides between get_media_buy_delivery_request (breakdown config) and + # sync_audiences_request (audience payload). aliases.py imports the request one directly. + "Audience": { + "get_media_buy_delivery_request", + "sync_audiences_request", + "sync_audiences_response", + }, } special_imports = [] @@ -110,7 +117,8 @@ def _module_sort_key(p: Path) -> tuple[int, str]: first_module = export_to_module[export_name] # Collision detected - skip this duplicate collisions.append( - f" {export_name}: defined in both {first_module} and {module_name} (using {first_module})" + f" {export_name}: defined in both " + f"{first_module} and {module_name} (using {first_module})" ) else: unique_exports.add(export_name) @@ -142,9 +150,11 @@ def _module_sort_key(p: Path) -> tuple[int, str]: qualified_name = ( f"_{type_name}From{parts[-1].replace('_', ' ').title().replace(' ', '')}" ) - special_imports.append( - f"from adcp.types.generated_poc.{module_name} import {type_name} as {qualified_name}" + import_str = ( + f"from adcp.types.generated_poc.{module_name}" + f" import {type_name} as {qualified_name}" ) + special_imports.append(import_str) all_exports.add(qualified_name) if collisions: @@ -158,7 +168,7 @@ def _module_sort_key(p: Path) -> tuple[int, str]: '"""INTERNAL: Consolidated generated types.', "", "DO NOT import from this module directly.", - "Use 'from adcp import Type' or 'from adcp.types.stable import Type' instead.", + "Use 'from adcp import Type' or 'from adcp.types import Type' instead.", "", "This module consolidates all generated types from generated_poc/ into a single", "namespace for convenience. The leading underscore signals this is private API.", @@ -182,7 +192,8 @@ def _module_sort_key(p: Path) -> tuple[int, str]: lines.extend( [ "", - "# Special imports for name collisions (qualified names for types defined in multiple modules)", + "# Special imports for name collisions" + " (qualified names for types defined in multiple modules)", ] ) lines.extend(special_imports) @@ -257,11 +268,58 @@ def _module_sort_key(p: Path) -> tuple[int, str]: "class AssetSelectors(_AdCPBaseModel):", ' model_config = _ConfigDict(extra="allow")', "", + "", + "# DeliverTo was removed in upstream schemas (v2.5.x). The GetSignalsRequest wire", + "# format changed: 'deliver_to' nested object with 'deployments'+'countries' was", + "# replaced by top-level 'destinations' and 'countries' fields. Code using", + "# GetSignalsRequest(deliver_to=DeliverTo(...)) must migrate to the new field names.", + "# This stub prevents ImportError only; it cannot preserve the old wire behavior.", + "class DeliverTo(_AdCPBaseModel):", + ' model_config = _ConfigDict(extra="allow")', + "", + "", + "class DeliverTo1(DeliverTo):", + " pass", + "", + "", + "# Pricing was removed in upstream schemas (v2.5.x). Replaced by SignalPricingOption", + "# which has the same cpm/currency fields. This stub preserves imports only.", + "class Pricing(_AdCPBaseModel):", + ' model_config = _ConfigDict(extra="allow")', + "", + "", + "# Measurement was renamed to OutcomeMeasurement upstream. This alias preserves imports.", + "Measurement = OutcomeMeasurement", + "", + "# ListAuthorizedProperties was removed from upstream schemas.", + "# Replaced by adagents.json-based authorization model.", + "class ListAuthorizedPropertiesRequest(_AdCPBaseModel):", + ' model_config = _ConfigDict(extra="allow")', + "", + "", + "class ListAuthorizedPropertiesResponse(_AdCPBaseModel):", + ' model_config = _ConfigDict(extra="allow")', + "", + "", + "# PackageStatus enum was removed from upstream schemas.", + "class PackageStatus(str, _Enum):", + " draft = 'draft'", + " active = 'active'", + " paused = 'paused'", + " completed = 'completed'", + "", ] lines.extend(compat_lines) all_exports_with_aliases = all_exports_with_aliases | { "AssetSelectors", "BrandManifest", + "DeliverTo", + "DeliverTo1", + "ListAuthorizedPropertiesRequest", + "ListAuthorizedPropertiesResponse", + "Measurement", + "PackageStatus", + "Pricing", "PromotedOfferings", "PromotedOfferingsAssetRequirements", "PromotedOfferingsRequirement", diff --git a/scripts/generate_ergonomic_coercion.py b/scripts/generate_ergonomic_coercion.py index 094143f0..02bcc21d 100644 --- a/scripts/generate_ergonomic_coercion.py +++ b/scripts/generate_ergonomic_coercion.py @@ -354,14 +354,15 @@ def generate_code() -> str: lines.append("from adcp.types.generated_poc.media_buy.create_media_buy_request import (") lines.append(" CreateMediaBuyRequest,") lines.append(")") - lines.append( - "from adcp.types.generated_poc.media_buy.get_products_request import GetProductsRequest" - ) + lines.append("from adcp.types.generated_poc.media_buy.get_products_request import (") + lines.append(" FieldModel,") + lines.append(" GetProductsRequest,") + lines.append(")") lines.append("from adcp.types.generated_poc.media_buy.list_creative_formats_request import (") lines.append(" ListCreativeFormatsRequest,") lines.append(")") lines.append("from adcp.types.generated_poc.media_buy.list_creatives_request import (") - lines.append(" FieldModel,") + lines.append(" Field1,") lines.append(" ListCreativesRequest,") lines.append(" Sort,") lines.append(")") diff --git a/src/adcp/__init__.py b/src/adcp/__init__.py index 58e0c8ff..3523105a 100644 --- a/src/adcp/__init__.py +++ b/src/adcp/__init__.py @@ -59,27 +59,43 @@ # Re-export core domain types and pricing options # These are commonly used in typical workflows from adcp.types import ( + # Account types + AccountReference, + # Type enums from PR #222 + AccountScope, # Audience & Targeting ActivateSignalRequest, ActivateSignalResponse, - # Type enums from PR #222 + # Creative types + ArtifactWebhookPayload, AssetContentType, AssetSelectors, + AudienceSource, # Core domain types BrandManifest, + BrandReference, # Creative Operations BuildCreativeRequest, BuildCreativeResponse, + BuyingMode, # Catalog types Catalog, CatalogAction, + CatalogFieldBinding, + CatalogFieldMapping, + CatalogGroupBinding, CatalogItemStatus, CatalogRequirements, CatalogType, + ConsentBasis, ContentIdType, + ContextObject, # Pricing options (all types for product creation) + CpaPricingOption, CpcPricingOption, CpcvPricingOption, + CpmAuctionPricingOption, + CpmFixedRatePricingOption, CpmPricingOption, CppPricingOption, CpvPricingOption, @@ -88,22 +104,37 @@ CreateMediaBuyResponse, Creative, CreativeApproval, + CreativeApprovalStatus, CreativeFilters, CreativeManifest, # Status enums (for control flow) CreativeStatus, + CreativeVariant, + DateRange, + DatetimeRange, + DeliverTo, DeliveryStatus, + DevicePlatform, + DeviceType, + Duration, # Common data types Error, + ErrorCode, + EventType, + ExtensionObject, FeedFormat, FlatRatePricingOption, Format, FormatCategory, FormatId, GeneratedTaskStatus, + GetAccountFinancialsRequest, + GetAccountFinancialsResponse, # Creative Delivery GetCreativeDeliveryRequest, GetCreativeDeliveryResponse, + GetCreativeFeaturesRequest, + GetCreativeFeaturesResponse, GetMediaBuyDeliveryRequest, GetMediaBuyDeliveryResponse, GetMediaBuysRequest, @@ -116,6 +147,8 @@ # Account Operations ListAccountsRequest, ListAccountsResponse, + ListAuthorizedPropertiesRequest, + ListAuthorizedPropertiesResponse, ListCreativeFormatsRequest, ListCreativeFormatsResponse, ListCreativesRequest, @@ -124,16 +157,30 @@ LogEventRequest, LogEventResponse, McpWebhookPayload, + Measurement, MediaBuy, + MediaBuyDeliveryStatus, MediaBuyPackage, MediaBuyStatus, + MediaChannel, OfferingAssetConstraint, OfferingAssetGroup, + # Optimization + OptimizationGoal, + # Format overlays + Overlay, Package, PackageRequest, + PackageStatus, + PaginationRequest, + PreviewCreativeFormatRequest, + PreviewCreativeInteractiveResponse, + PreviewCreativeManifestRequest, PreviewCreativeRequest, PreviewCreativeResponse, + PreviewCreativeStaticResponse, PriceGuidance, + Pricing, PricingModel, Product, ProductFilters, @@ -142,15 +189,25 @@ PromotedOfferingsRequirement, PromotedProducts, Property, + PropertyIdActivationKey, + PropertyTagActivationKey, + Proposal, ProvidePerformanceFeedbackRequest, ProvidePerformanceFeedbackResponse, PushNotificationConfig, + Refine, + ReportUsageRequest, + ReportUsageResponse, SignalCatalogType, SignalFilters, + SignalPricing, + SignalPricingOption, Snapshot, SnapshotUnavailableReason, SyncAccountsRequest, SyncAccountsResponse, + SyncAudiencesRequest, + SyncAudiencesResponse, SyncCatalogsInputRequired, SyncCatalogsRequest, SyncCatalogsResponse, @@ -160,10 +217,16 @@ SyncCreativesResponse, SyncEventSourcesRequest, SyncEventSourcesResponse, + TimeBasedPricingOption, + TimeUnit, + Transform, UpdateFrequency, UpdateMediaBuyRequest, UpdateMediaBuyResponse, + VcpmAuctionPricingOption, + VcpmFixedRatePricingOption, VcpmPricingOption, + WcagLevel, aliases, ) @@ -173,61 +236,96 @@ # Re-export semantic type aliases for better ergonomics from adcp.types.aliases import ( + AccountReferenceById, + AccountReferenceByNaturalKey, ActivateSignalErrorResponse, ActivateSignalSuccessResponse, AgentDeployment, AgentDestination, + AuthorizedAgent, + AuthorizedAgentsByInlineProperties, + AuthorizedAgentsByPropertyId, + AuthorizedAgentsByPropertyTag, + AuthorizedAgentsByPublisherProperties, + AuthorizedAgentsBySignalId, + AuthorizedAgentsBySignalTag, BothPreviewRender, BuildCreativeErrorResponse, BuildCreativeSuccessResponse, CalibrateContentErrorResponse, CalibrateContentSuccessResponse, + CpmSignalPricingOption, CreateContentStandardsErrorResponse, CreateContentStandardsSuccessResponse, CreateMediaBuyErrorResponse, CreateMediaBuySuccessResponse, Deployment, Destination, + FlatFeeSignalPricingOption, + GetAccountFinancialsErrorResponse, + GetAccountFinancialsSuccessResponse, GetContentStandardsErrorResponse, GetContentStandardsSuccessResponse, GetCreativeDeliveryByBuyerRefRequest, GetCreativeDeliveryByCreativeRequest, GetCreativeDeliveryByMediaBuyRequest, + GetCreativeFeaturesErrorResponse, + GetCreativeFeaturesSuccessResponse, GetMediaBuyArtifactsErrorResponse, GetMediaBuyArtifactsSuccessResponse, + GetProductsBriefRequest, + GetProductsRefineRequest, + GetProductsWholesaleRequest, + GetSignalsDiscoveryRequest, + GetSignalsLookupRequest, HtmlPreviewRender, InlineDaastAsset, InlineVastAsset, + KeyValueActivationKey, ListContentStandardsErrorResponse, ListContentStandardsSuccessResponse, LogEventErrorResponse, LogEventSuccessResponse, MediaSubAsset, + PercentOfMediaSignalPricingOption, PlatformDeployment, PlatformDestination, - PreviewCreativeFormatRequest, - PreviewCreativeInteractiveResponse, - PreviewCreativeManifestRequest, - PreviewCreativeStaticResponse, + PreviewCreativeBatchRequest, + PreviewCreativeBatchResponse, + PreviewCreativeSingleRequest, + PreviewCreativeSingleResponse, + PreviewCreativeVariantRequest, + PreviewCreativeVariantResponse, PricingOption, PropertyId, PropertyTag, + ProvidePerformanceFeedbackByBuyerRefRequest, + ProvidePerformanceFeedbackByMediaBuyRequest, ProvidePerformanceFeedbackErrorResponse, ProvidePerformanceFeedbackSuccessResponse, PublisherProperties, PublisherPropertiesAll, PublisherPropertiesById, PublisherPropertiesByTag, + SegmentIdActivationKey, + SiSendActionResponseRequest, + SiSendTextMessageRequest, SyncAccountsErrorResponse, SyncAccountsSuccessResponse, + SyncAudiencesAudience, + SyncAudiencesErrorResponse, + SyncAudiencesSuccessResponse, SyncCatalogResult, SyncCatalogsErrorResponse, SyncCatalogsSuccessResponse, + SyncCreativeResult, SyncCreativesErrorResponse, SyncCreativesSuccessResponse, SyncEventSourcesErrorResponse, SyncEventSourcesSuccessResponse, TextSubAsset, + UpdateContentStandardsErrorResponse, + UpdateContentStandardsSuccessResponse, UpdateMediaBuyErrorResponse, UpdateMediaBuyPackagesRequest, UpdateMediaBuyPropertiesRequest, @@ -319,11 +417,23 @@ def get_adcp_version() -> str: "get_adcp_signed_headers_for_webhook", "extract_webhook_result_data", "McpWebhookPayload", + # Account operations + "AccountReference", + "GetAccountFinancialsRequest", + "GetAccountFinancialsResponse", + "GetAccountFinancialsSuccessResponse", + "GetAccountFinancialsErrorResponse", + "ReportUsageRequest", + "ReportUsageResponse", # Common request/response types (re-exported for convenience) "CreateMediaBuyRequest", "CreateMediaBuyResponse", "GetCreativeDeliveryRequest", "GetCreativeDeliveryResponse", + "GetCreativeFeaturesRequest", + "GetCreativeFeaturesResponse", + "GetCreativeFeaturesSuccessResponse", + "GetCreativeFeaturesErrorResponse", "GetMediaBuyDeliveryRequest", "GetMediaBuyDeliveryResponse", "GetMediaBuysRequest", @@ -346,6 +456,8 @@ def get_adcp_version() -> str: "PreviewCreativeResponse", "SyncAccountsRequest", "SyncAccountsResponse", + "SyncAudiencesRequest", + "SyncAudiencesResponse", "SyncCatalogsInputRequired", "SyncCatalogsRequest", "SyncCatalogsResponse", @@ -372,14 +484,18 @@ def get_adcp_version() -> str: # Catalog types "Catalog", "CatalogAction", + "CatalogFieldBinding", + "CatalogFieldMapping", "CatalogItemStatus", "CatalogRequirements", "CatalogType", + "ConsentBasis", "ContentIdType", "FeedFormat", "Gtin", "OfferingAssetConstraint", "OfferingAssetGroup", + "Transform", "UpdateFrequency", # Backward compat: these types were removed from upstream schemas "AssetSelectors", @@ -390,23 +506,49 @@ def get_adcp_version() -> str: "Property", "SignalCatalogType", # Core domain types (from stable API) + "AccountScope", + "ArtifactWebhookPayload", + "AudienceSource", "BrandManifest", + "BrandReference", + "BuyingMode", + "CatalogGroupBinding", + "ContextObject", "Creative", "CreativeApproval", + "CreativeApprovalStatus", "CreativeFilters", "CreativeManifest", + "CreativeVariant", + "DateRange", + "DatetimeRange", + "DevicePlatform", + "DeviceType", + "Duration", + "ErrorCode", + "EventType", + "ExtensionObject", "MediaBuy", + "MediaBuyDeliveryStatus", "MediaBuyPackage", + "MediaChannel", + "OptimizationGoal", + "Overlay", "Package", "PackageRequest", + "PaginationRequest", + "Proposal", + "Refine", "Snapshot", "SnapshotUnavailableReason", + "WcagLevel", # Status enums (for control flow) "CreativeStatus", "DeliveryStatus", "MediaBuyStatus", "PricingModel", # Pricing-related types + "CpaPricingOption", "CpcPricingOption", "CpcvPricingOption", "CpmPricingOption", @@ -414,7 +556,15 @@ def get_adcp_version() -> str: "CpvPricingOption", "FlatRatePricingOption", "PriceGuidance", + "TimeBasedPricingOption", + "TimeUnit", "VcpmPricingOption", + # Signal pricing types + "SignalPricing", + "SignalPricingOption", + "CpmSignalPricingOption", + "FlatFeeSignalPricingOption", + "PercentOfMediaSignalPricingOption", # Configuration types "PushNotificationConfig", # Adagents validation @@ -476,10 +626,19 @@ def get_adcp_version() -> str: "aliases", "GeneratedTaskStatus", # Semantic type aliases (for better API ergonomics) + "AccountReferenceById", + "AccountReferenceByNaturalKey", "ActivateSignalSuccessResponse", "ActivateSignalErrorResponse", "AgentDeployment", "AgentDestination", + "AuthorizedAgent", + "AuthorizedAgentsByInlineProperties", + "AuthorizedAgentsByPropertyId", + "AuthorizedAgentsByPropertyTag", + "AuthorizedAgentsByPublisherProperties", + "AuthorizedAgentsBySignalId", + "AuthorizedAgentsBySignalTag", "BothPreviewRender", "BuildCreativeSuccessResponse", "BuildCreativeErrorResponse", @@ -498,9 +657,15 @@ def get_adcp_version() -> str: "GetCreativeDeliveryByMediaBuyRequest", "GetMediaBuyArtifactsSuccessResponse", "GetMediaBuyArtifactsErrorResponse", + "GetProductsBriefRequest", + "GetProductsRefineRequest", + "GetProductsWholesaleRequest", + "GetSignalsDiscoveryRequest", + "GetSignalsLookupRequest", "HtmlPreviewRender", "InlineDaastAsset", "InlineVastAsset", + "KeyValueActivationKey", "ListContentStandardsSuccessResponse", "ListContentStandardsErrorResponse", "LogEventSuccessResponse", @@ -508,29 +673,42 @@ def get_adcp_version() -> str: "MediaSubAsset", "PlatformDeployment", "PlatformDestination", - "PreviewCreativeFormatRequest", - "PreviewCreativeManifestRequest", - "PreviewCreativeStaticResponse", - "PreviewCreativeInteractiveResponse", + "PreviewCreativeBatchRequest", + "PreviewCreativeBatchResponse", + "PreviewCreativeSingleRequest", + "PreviewCreativeSingleResponse", + "PreviewCreativeVariantRequest", + "PreviewCreativeVariantResponse", "PricingOption", "PropertyId", "PropertyTag", + "ProvidePerformanceFeedbackByBuyerRefRequest", + "ProvidePerformanceFeedbackByMediaBuyRequest", "ProvidePerformanceFeedbackSuccessResponse", "ProvidePerformanceFeedbackErrorResponse", "PublisherProperties", "PublisherPropertiesAll", "PublisherPropertiesById", "PublisherPropertiesByTag", + "SegmentIdActivationKey", + "SiSendActionResponseRequest", + "SiSendTextMessageRequest", "SyncAccountsSuccessResponse", "SyncAccountsErrorResponse", + "SyncAudiencesAudience", + "SyncAudiencesSuccessResponse", + "SyncAudiencesErrorResponse", "SyncCatalogResult", "SyncCatalogsSuccessResponse", "SyncCatalogsErrorResponse", + "SyncCreativeResult", "SyncCreativesSuccessResponse", "SyncCreativesErrorResponse", "SyncEventSourcesSuccessResponse", "SyncEventSourcesErrorResponse", "TextSubAsset", + "UpdateContentStandardsSuccessResponse", + "UpdateContentStandardsErrorResponse", "UpdateMediaBuySuccessResponse", "UpdateMediaBuyErrorResponse", "UpdateMediaBuyPackagesRequest", @@ -540,4 +718,25 @@ def get_adcp_version() -> str: "UrlVastAsset", "ValidateContentDeliverySuccessResponse", "ValidateContentDeliveryErrorResponse", + # Backward compat: renamed/removed types + "DeliverTo", + "Measurement", + "Pricing", + # Backward compat: pricing type renames + "CpmAuctionPricingOption", + "CpmFixedRatePricingOption", + "VcpmAuctionPricingOption", + "VcpmFixedRatePricingOption", + # Backward compat: activation key schema change + "PropertyIdActivationKey", + "PropertyTagActivationKey", + # Backward compat: preview alias renames + "PreviewCreativeFormatRequest", + "PreviewCreativeInteractiveResponse", + "PreviewCreativeManifestRequest", + "PreviewCreativeStaticResponse", + # Backward compat: types removed from upstream schemas + "ListAuthorizedPropertiesRequest", + "ListAuthorizedPropertiesResponse", + "PackageStatus", ] diff --git a/src/adcp/types/__init__.py b/src/adcp/types/__init__.py index 9c924a98..e6dd78d6 100644 --- a/src/adcp/types/__init__.py +++ b/src/adcp/types/__init__.py @@ -3,12 +3,15 @@ All AdCP types exported from a single location. Users should import from here or directly from adcp. -Examples: from adcp.types import Product, CreativeFilters from adcp import Product, CreativeFilters +IMPORTANT: Never import directly from adcp.types.generated_poc or +adcp.types._generated. These are internal modules regenerated from +upstream schemas. Only import from adcp.types (this module) or adcp. + Type Coercion: - For developer ergonomics, request types accept flexible input: + Request types accept flexible input for developer ergonomics: - Enum fields accept string values: ListCreativeFormatsRequest(type="video") # Works! @@ -20,7 +23,7 @@ - FieldModel lists accept strings: ListCreativesRequest(fields=["creative_id", "name"]) # Works! - See adcp.types.coercion for implementation details. + See adcp.types._ergonomic for implementation details. """ from __future__ import annotations @@ -39,33 +42,43 @@ # V3 Content Standards types # V3 Sponsored Intelligence types # V3 Governance (Property Lists) types +# Re-export webhook payload type for webhook handling from adcp.types._generated import ( A2UiComponent, A2UiSurface, Account, + AccountReference, + AccountScope, ActivateSignalRequest, ActivateSignalResponse, AggregatedTotals, Artifact, + ArtifactWebhookPayload, Asset, AssetContentType, AssetSelectors, AssignedPackage, Assignments, + AudienceSource, AudioAsset, Authentication, AuthenticationScheme, AuthorizedAgents, AvailableMetric, BrandManifest, + BrandReference, BuildCreativeRequest, BuildCreativeResponse, + BuyingMode, ByCatalogItemItem, ByPackageItem, CalibrateContentRequest, CalibrateContentResponse, Catalog, CatalogAction, + CatalogFieldBinding, + CatalogFieldBinding1, + CatalogFieldMapping, CatalogItemStatus, CatalogRequirements, CatalogType, @@ -74,7 +87,9 @@ Contact, ContentIdType, ContentStandards, + ContextObject, Country, + CpaPricingOption, CpcPricingOption, CpcvPricingOption, CpmPricingOption, @@ -91,16 +106,20 @@ CreativeAgent, CreativeAgentCapability, CreativeApproval, + CreativeApprovalStatus, CreativeAsset, CreativeAssignment, CreativeFilters, CreativeManifest, CreativePolicy, CreativeStatus, + CreativeVariant, CssAsset, DaastTrackingEvent, DaastVersion, DailyBreakdownItem, + DateRange, + DatetimeRange, DayOfWeek, DaypartTarget, DeletePropertyListRequest, @@ -112,12 +131,19 @@ DeliveryStatus, DeliveryType, DemographicSystem, + DevicePlatform, + DeviceType, DimensionUnit, Disclaimer, DoohMetrics, + Duration, Error, + ErrorCode, + EventType, + ExtensionObject, FeedbackSource, FeedFormat, + Field1, FieldModel, FlatRatePricingOption, Fonts, @@ -138,12 +164,16 @@ GeoMetro, GeoPostalArea, GeoRegion, + GetAccountFinancialsRequest, + GetAccountFinancialsResponse, GetAdcpCapabilitiesRequest, GetAdcpCapabilitiesResponse, GetContentStandardsRequest, GetContentStandardsResponse, GetCreativeDeliveryRequest, GetCreativeDeliveryResponse, + GetCreativeFeaturesRequest, + GetCreativeFeaturesResponse, GetMediaBuyArtifactsRequest, GetMediaBuyArtifactsResponse, GetMediaBuyDeliveryRequest, @@ -167,6 +197,8 @@ LandingPageRequirement, ListAccountsRequest, ListAccountsResponse, + ListAuthorizedPropertiesRequest, + ListAuthorizedPropertiesResponse, ListContentStandardsRequest, ListContentStandardsResponse, ListCreativeFormatsRequest, @@ -179,22 +211,28 @@ LogEventResponse, Logo, MarkdownFlavor, - Measurement, + McpWebhookPayload, MeasurementPeriod, MediaBuy, MediaBuyDelivery, MediaBuyPackage, MediaBuyStatus, + MediaChannel, Metadata, MetricType, NotificationType, Offering, OfferingAssetConstraint, OfferingAssetGroup, + OptimizationGoal, + OutcomeMeasurement, + Overlay, Pacing, PackageRequest, + PackageStatus, PackageUpdate, Pagination, + PaginationRequest, Parameters, Performance, PerformanceFeedback, @@ -224,6 +262,7 @@ PropertyListFilters, PropertyListReference, PropertyType, + Proposal, ProtocolEnvelope, ProtocolResponse, ProvidePerformanceFeedbackRequest, @@ -234,10 +273,13 @@ QuartileData, QuerySummary, ReachUnit, + Refine, ReportingCapabilities, ReportingFrequency, ReportingPeriod, ReportingWebhook, + ReportUsageRequest, + ReportUsageResponse, Request, Response, Responsive, @@ -249,6 +291,8 @@ Signal, SignalCatalogType, SignalFilters, + SignalPricing, + SignalPricingOption, SiIdentity, SiInitiateSessionRequest, SiInitiateSessionResponse, @@ -262,10 +306,11 @@ Sort, SortApplied, SortDirection, - Status, StatusSummary, SyncAccountsRequest, SyncAccountsResponse, + SyncAudiencesRequest, + SyncAudiencesResponse, SyncCatalogsInputRequired, SyncCatalogsRequest, SyncCatalogsResponse, @@ -279,7 +324,10 @@ TargetingOverlay, TaskType, TextAsset, + TimeBasedPricingOption, + TimeUnit, Totals, + Transform, UpdateContentStandardsRequest, UpdateContentStandardsResponse, UpdateFrequency, @@ -298,6 +346,7 @@ VenueBreakdownItem, VideoAsset, ViewThreshold, + WcagLevel, WebhookAsset, WebhookResponseType, ) @@ -306,6 +355,8 @@ # Import semantic aliases for discriminated unions from adcp.types.aliases import ( + AccountReferenceById, + AccountReferenceByNaturalKey, ActivateSignalErrorResponse, ActivateSignalSuccessResponse, AgentDeployment, @@ -315,49 +366,77 @@ AuthorizedAgentsByPropertyId, AuthorizedAgentsByPropertyTag, AuthorizedAgentsByPublisherProperties, + AuthorizedAgentsBySignalId, + AuthorizedAgentsBySignalTag, BothPreviewRender, BuildCreativeErrorResponse, BuildCreativeSuccessResponse, CalibrateContentErrorResponse, CalibrateContentSuccessResponse, + CatalogGroupBinding, + ConsentBasis, + CpmSignalPricingOption, CreateContentStandardsErrorResponse, CreateContentStandardsSuccessResponse, CreateMediaBuyErrorResponse, CreateMediaBuySuccessResponse, Deployment, Destination, + FlatFeeSignalPricingOption, + GetAccountFinancialsErrorResponse, + GetAccountFinancialsSuccessResponse, GetContentStandardsErrorResponse, GetContentStandardsSuccessResponse, GetCreativeDeliveryByBuyerRefRequest, GetCreativeDeliveryByCreativeRequest, GetCreativeDeliveryByMediaBuyRequest, + GetCreativeFeaturesErrorResponse, + GetCreativeFeaturesSuccessResponse, GetMediaBuyArtifactsErrorResponse, GetMediaBuyArtifactsSuccessResponse, + GetProductsBriefRequest, + GetProductsRefineRequest, + GetProductsWholesaleRequest, + GetSignalsDiscoveryRequest, + GetSignalsLookupRequest, HtmlPreviewRender, InlineDaastAsset, InlineVastAsset, + KeyValueActivationKey, ListContentStandardsErrorResponse, ListContentStandardsSuccessResponse, LogEventErrorResponse, LogEventSuccessResponse, + MediaBuyDeliveryStatus, MediaSubAsset, + PercentOfMediaSignalPricingOption, PlatformDeployment, PlatformDestination, - PreviewCreativeFormatRequest, - PreviewCreativeInteractiveResponse, - PreviewCreativeManifestRequest, - PreviewCreativeStaticResponse, + PreviewCreativeBatchRequest, + PreviewCreativeBatchResponse, + PreviewCreativeSingleRequest, + PreviewCreativeSingleResponse, + PreviewCreativeVariantRequest, + PreviewCreativeVariantResponse, PricingOption, PropertyId, PropertyTag, + ProvidePerformanceFeedbackByBuyerRefRequest, + ProvidePerformanceFeedbackByMediaBuyRequest, ProvidePerformanceFeedbackErrorResponse, ProvidePerformanceFeedbackSuccessResponse, PublisherProperties, PublisherPropertiesAll, PublisherPropertiesById, PublisherPropertiesByTag, + SegmentIdActivationKey, + SiSendActionResponseRequest, + SiSendTextMessageRequest, SyncAccountsErrorResponse, SyncAccountsSuccessResponse, + SyncAudiencesAudience, + SyncAudiencesErrorResponse, + SyncAudiencesSuccessResponse, SyncCatalogResult, SyncCatalogsErrorResponse, SyncCatalogsSuccessResponse, @@ -367,6 +446,8 @@ SyncEventSourcesErrorResponse, SyncEventSourcesSuccessResponse, TextSubAsset, + UpdateContentStandardsErrorResponse, + UpdateContentStandardsSuccessResponse, UpdateMediaBuyErrorResponse, UpdateMediaBuyPackagesRequest, UpdateMediaBuyPropertiesRequest, @@ -391,11 +472,34 @@ WebhookMetadata, ) -# Re-export webhook payload type for webhook handling -from adcp.types.generated_poc.core.mcp_webhook_payload import McpWebhookPayload +# Status: _generated picks invoice status (get_account_financials_response) due to +# alphabetical module sort. Import the delivery status variant directly for backward compat. +from adcp.types.generated_poc.media_buy.get_media_buy_delivery_response import ( # noqa: E501 + Status, +) + +# Semantic aliases for auto-generated field enum names +ListCreativesField = Field1 # Backward compatibility aliases AssetType = AssetContentType # Use AssetContentType instead +Measurement = OutcomeMeasurement # Renamed upstream to OutcomeMeasurement + +# Pricing type renames: upstream merged auction/fixed variants into single types +CpmAuctionPricingOption = CpmPricingOption +CpmFixedRatePricingOption = CpmPricingOption +VcpmAuctionPricingOption = VcpmPricingOption +VcpmFixedRatePricingOption = VcpmPricingOption + +# Activation key schema change: property_id/property_tag → segment_id/key_value +PropertyIdActivationKey = SegmentIdActivationKey +PropertyTagActivationKey = KeyValueActivationKey + +# Preview alias renames: format/manifest → single/batch/variant discriminator +PreviewCreativeFormatRequest = PreviewCreativeSingleRequest +PreviewCreativeManifestRequest = PreviewCreativeBatchRequest +PreviewCreativeStaticResponse = PreviewCreativeSingleResponse +PreviewCreativeInteractiveResponse = PreviewCreativeBatchResponse # Schema renames from filter ref split (v1.0.0) Action = CreativeAction @@ -420,8 +524,16 @@ "A2UiSurface", # Account types "Account", + "AccountReference", + "AccountReferenceById", + "AccountReferenceByNaturalKey", + "AccountScope", + "GetAccountFinancialsRequest", + "GetAccountFinancialsResponse", "ListAccountsRequest", "ListAccountsResponse", + "ReportUsageRequest", + "ReportUsageResponse", "SyncAccountsRequest", "SyncAccountsResponse", # Request/Response types @@ -456,6 +568,7 @@ "ProvidePerformanceFeedbackRequest", "ProvidePerformanceFeedbackResponse", "QuerySummary", + "Refine", "SortApplied", "StatusSummary", "SyncCatalogsInputRequired", @@ -463,6 +576,10 @@ "SyncCatalogsResponse", "SyncCatalogsSubmitted", "SyncCatalogsWorking", + "SyncAudiencesAudience", + "SyncAudiencesRequest", + "SyncAudiencesResponse", + "ConsentBasis", "SyncCreativesRequest", "SyncCreativesResponse", # Event & Source Operations @@ -479,6 +596,9 @@ # Creative Delivery "GetCreativeDeliveryRequest", "GetCreativeDeliveryResponse", + # Creative Features + "GetCreativeFeaturesRequest", + "GetCreativeFeaturesResponse", # Forecasting types "DayOfWeek", "DaypartTarget", @@ -492,6 +612,7 @@ "ReachUnit", # V3 Content Standards "Artifact", + "ArtifactWebhookPayload", "CalibrateContentRequest", "CalibrateContentResponse", "ContentStandards", @@ -537,6 +658,10 @@ # Catalog types "Catalog", "CatalogAction", + "CatalogFieldBinding", + "CatalogFieldBinding1", + "CatalogGroupBinding", + "CatalogFieldMapping", "CatalogItemStatus", "CatalogRequirements", "CatalogType", @@ -549,6 +674,13 @@ "AssetSelectors", "AssetContentType", "AssetType", # Deprecated + "AudienceSource", + "BrandReference", + "BuyingMode", + "ContextObject", + "DateRange", + "DatetimeRange", + "Duration", "FormatCategory", "AssignedPackage", "Assignments", @@ -560,17 +692,24 @@ "Creative", "CreativeAgent", "CreativeApproval", + "CreativeApprovalStatus", "CreativeAsset", "CreativeAssignment", "CreativeFilters", "CreativeManifest", "CreativePolicy", + "CreativeVariant", + "DevicePlatform", + "DeviceType", "DeliveryMeasurement", "DeliveryMetrics", "DeliveryStatus", "Disclaimer", "DoohMetrics", "Error", + "ErrorCode", + "EventType", + "ExtensionObject", "FeedFormat", "Fonts", "Format", @@ -582,10 +721,15 @@ "Input", "LandingPageRequirement", "Logo", + "ListCreativesField", "MediaBuy", "MediaBuyPackage", + "MediaChannel", "Metadata", "Offering", + "OptimizationGoal", + "OutcomeMeasurement", + "Overlay", "Package", "PackageRequest", "PackageUpdate", @@ -598,6 +742,7 @@ "Pricing", "Product", "ProductCard", + "Proposal", "ProductCardDetailed", "ProductCatalog", "ProductFilters", @@ -612,12 +757,19 @@ "Results", "Signal", "SignalFilters", + "SignalPricing", + "SignalPricingOption", + "CpmSignalPricingOption", + "FlatFeeSignalPricingOption", + "PercentOfMediaSignalPricingOption", "Snapshot", "SnapshotUnavailableReason", "Tags", "TargetingOverlay", + "Transform", "VenueBreakdownItem", # Pricing types + "CpaPricingOption", "CpcPricingOption", "CpcvPricingOption", "CpmPricingOption", @@ -625,6 +777,8 @@ "CpvPricingOption", "FlatRatePricingOption", "PriceGuidance", + "TimeBasedPricingOption", + "TimeUnit", "VcpmPricingOption", # Status enums & simple types "SignalCatalogType", @@ -643,7 +797,6 @@ "GeoPostalArea", "GeoRegion", "MarkdownFlavor", - "Measurement", "MeasurementPeriod", "MediaBuyStatus", "HttpMethod", @@ -653,6 +806,7 @@ "PreviewOutputFormat", "Pacing", "Pagination", + "PaginationRequest", "PricingModel", "PrimaryCountry", "PropertyIdentifierTypes", @@ -672,6 +826,7 @@ "ValidationMode", "VastVersion", "ViewThreshold", + "WcagLevel", # Configuration & infrastructure types "Authentication", "AuthorizedAgents", @@ -713,6 +868,8 @@ "AuthorizedAgentsByPropertyId", "AuthorizedAgentsByPropertyTag", "AuthorizedAgentsByPublisherProperties", + "AuthorizedAgentsBySignalId", + "AuthorizedAgentsBySignalTag", "BothPreviewRender", "BuildCreativeErrorResponse", "BuildCreativeSuccessResponse", @@ -731,6 +888,11 @@ "GetCreativeDeliveryByMediaBuyRequest", "GetMediaBuyArtifactsErrorResponse", "GetMediaBuyArtifactsSuccessResponse", + "GetProductsBriefRequest", + "GetProductsRefineRequest", + "GetProductsWholesaleRequest", + "GetSignalsDiscoveryRequest", + "GetSignalsLookupRequest", "HtmlPreviewRender", "InlineDaastAsset", "InlineVastAsset", @@ -741,19 +903,27 @@ "MediaSubAsset", "PlatformDeployment", "PlatformDestination", - "PreviewCreativeFormatRequest", - "PreviewCreativeInteractiveResponse", - "PreviewCreativeManifestRequest", - "PreviewCreativeStaticResponse", + "PreviewCreativeBatchRequest", + "PreviewCreativeBatchResponse", + "PreviewCreativeSingleRequest", + "PreviewCreativeSingleResponse", + "PreviewCreativeVariantRequest", + "PreviewCreativeVariantResponse", "PricingOption", "PropertyId", "PropertyTag", + "ProvidePerformanceFeedbackByBuyerRefRequest", + "ProvidePerformanceFeedbackByMediaBuyRequest", "ProvidePerformanceFeedbackErrorResponse", "ProvidePerformanceFeedbackSuccessResponse", "PublisherProperties", "PublisherPropertiesAll", "PublisherPropertiesById", "PublisherPropertiesByTag", + "SegmentIdActivationKey", + "KeyValueActivationKey", + "SiSendTextMessageRequest", + "SiSendActionResponseRequest", "SyncAccountsErrorResponse", "SyncAccountsSuccessResponse", "SyncCatalogResult", @@ -765,6 +935,8 @@ "SyncEventSourcesErrorResponse", "SyncEventSourcesSuccessResponse", "TextSubAsset", + "UpdateContentStandardsErrorResponse", + "UpdateContentStandardsSuccessResponse", "UpdateMediaBuyErrorResponse", "UpdateMediaBuyPackagesRequest", "UpdateMediaBuyPropertiesRequest", @@ -774,6 +946,16 @@ "UrlVastAsset", "ValidateContentDeliveryErrorResponse", "ValidateContentDeliverySuccessResponse", + # Account financials responses + "GetAccountFinancialsErrorResponse", + "GetAccountFinancialsSuccessResponse", + # Audiences responses + "MediaBuyDeliveryStatus", + "SyncAudiencesErrorResponse", + "SyncAudiencesSuccessResponse", + # Creative features responses + "GetCreativeFeaturesErrorResponse", + "GetCreativeFeaturesSuccessResponse", # Internal/special exports "GeneratedTaskStatus", # Backward compatibility aliases (deprecated) @@ -782,6 +964,7 @@ "CoBranding", "FormatType", "LandingPage", + "Measurement", "Method", "ModuleType", "TrackingEvent", @@ -792,6 +975,23 @@ "SignalType", "UrlType", "AvailableReportingFrequency", + # Backward compat: pricing type renames (auction/fixed merged into single types) + "CpmAuctionPricingOption", + "CpmFixedRatePricingOption", + "VcpmAuctionPricingOption", + "VcpmFixedRatePricingOption", + # Backward compat: activation key schema change + "PropertyIdActivationKey", + "PropertyTagActivationKey", + # Backward compat: preview alias renames + "PreviewCreativeFormatRequest", + "PreviewCreativeManifestRequest", + "PreviewCreativeStaticResponse", + "PreviewCreativeInteractiveResponse", + # Backward compat: types removed from upstream schemas + "ListAuthorizedPropertiesRequest", + "ListAuthorizedPropertiesResponse", + "PackageStatus", # Submodules for advanced use: "generated", "aliases", diff --git a/src/adcp/types/_ergonomic.py b/src/adcp/types/_ergonomic.py index 280c0c28..93bd6ff0 100644 --- a/src/adcp/types/_ergonomic.py +++ b/src/adcp/types/_ergonomic.py @@ -52,6 +52,7 @@ from adcp.types.generated_poc.core.product import Product from adcp.types.generated_poc.enums.asset_content_type import AssetContentType from adcp.types.generated_poc.enums.creative_sort_field import CreativeSortField +from adcp.types.generated_poc.enums.disclosure_position import DisclosurePosition from adcp.types.generated_poc.enums.format_category import FormatCategory from adcp.types.generated_poc.enums.pacing import Pacing from adcp.types.generated_poc.enums.sort_direction import SortDirection @@ -59,12 +60,11 @@ from adcp.types.generated_poc.media_buy.create_media_buy_request import ( CreateMediaBuyRequest, ) -from adcp.types.generated_poc.media_buy.get_products_request import GetProductsRequest from adcp.types.generated_poc.media_buy.list_creative_formats_request import ( ListCreativeFormatsRequest, ) from adcp.types.generated_poc.media_buy.list_creatives_request import ( - FieldModel, + Field1, ListCreativesRequest, Sort, ) @@ -101,6 +101,7 @@ def _apply_coercion() -> None: # Apply coercion to ListCreativeFormatsRequest # - asset_types: list[AssetContentType | str] | None # - context: ContextObject | dict | None + # - disclosure_positions: list[DisclosurePosition | str] | None # - ext: ExtensionObject | dict | None # - type: FormatCategory | str | None # - wcag_level: WcagLevel | str | None @@ -117,6 +118,14 @@ def _apply_coercion() -> None: "context", Annotated[ContextObject | None, BeforeValidator(coerce_to_model(ContextObject))], ) + _patch_field_annotation( + ListCreativeFormatsRequest, + "disclosure_positions", + Annotated[ + list[DisclosurePosition] | None, + BeforeValidator(coerce_to_enum_list(DisclosurePosition)), + ], + ) _patch_field_annotation( ListCreativeFormatsRequest, "ext", @@ -137,7 +146,7 @@ def _apply_coercion() -> None: # Apply coercion to ListCreativesRequest # - context: ContextObject | dict | None # - ext: ExtensionObject | dict | None - # - fields: list[FieldModel | str] | None + # - fields: list[Field1 | str] | None _patch_field_annotation( ListCreativesRequest, "context", @@ -152,8 +161,8 @@ def _apply_coercion() -> None: ListCreativesRequest, "fields", Annotated[ - list[FieldModel] | None, - BeforeValidator(coerce_to_enum_list(FieldModel)), + list[Field1] | None, + BeforeValidator(coerce_to_enum_list(Field1)), ], ) ListCreativesRequest.model_rebuild(force=True) @@ -173,21 +182,6 @@ def _apply_coercion() -> None: ) Sort.model_rebuild(force=True) - # Apply coercion to GetProductsRequest - # - context: ContextObject | dict | None - # - ext: ExtensionObject | dict | None - _patch_field_annotation( - GetProductsRequest, - "context", - Annotated[ContextObject | None, BeforeValidator(coerce_to_model(ContextObject))], - ) - _patch_field_annotation( - GetProductsRequest, - "ext", - Annotated[ExtensionObject | None, BeforeValidator(coerce_to_model(ExtensionObject))], - ) - GetProductsRequest.model_rebuild(force=True) - # Apply coercion to PackageRequest # - creative_assignments: list[CreativeAssignment] (accepts subclass instances) # - creatives: list[CreativeAsset] (accepts subclass instances) diff --git a/src/adcp/types/_generated.py b/src/adcp/types/_generated.py index 5c5eeda6..cc3fe088 100644 --- a/src/adcp/types/_generated.py +++ b/src/adcp/types/_generated.py @@ -1,7 +1,7 @@ """INTERNAL: Consolidated generated types. DO NOT import from this module directly. -Use 'from adcp import Type' or 'from adcp.types.stable import Type' instead. +Use 'from adcp import Type' or 'from adcp.types import Type' instead. This module consolidates all generated types from generated_poc/ into a single namespace for convenience. The leading underscore signals this is private API. @@ -10,7 +10,7 @@ DO NOT EDIT MANUALLY. Generated from: https://github.com/adcontextprotocol/adcp/tree/main/schemas -Generation date: 2026-02-23 02:07:11 UTC +Generation date: 2026-02-28 18:37:35 UTC """ # ruff: noqa: E501, I001 @@ -22,6 +22,7 @@ from adcp.types.generated_poc.enums.age_verification_method import AgeVerificationMethod from adcp.types.generated_poc.enums.asset_content_type import AssetContentType from adcp.types.generated_poc.enums.attribution_model import AttributionModel +from adcp.types.generated_poc.enums.audience_source import AudienceSource from adcp.types.generated_poc.enums.auth_scheme import AuthenticationScheme from adcp.types.generated_poc.enums.available_metric import AvailableMetric from adcp.types.generated_poc.enums.catalog_action import CatalogAction @@ -29,6 +30,7 @@ from adcp.types.generated_poc.enums.catalog_type import CatalogType from adcp.types.generated_poc.enums.channels import MediaChannel from adcp.types.generated_poc.enums.co_branding_requirement import CoBrandingRequirement +from adcp.types.generated_poc.enums.consent_basis import ConsentBasis from adcp.types.generated_poc.enums.content_id_type import ContentIdType from adcp.types.generated_poc.enums.creative_action import CreativeAction from adcp.types.generated_poc.enums.creative_agent_capability import CreativeAgentCapability @@ -41,8 +43,12 @@ from adcp.types.generated_poc.enums.delivery_type import DeliveryType from adcp.types.generated_poc.enums.demographic_system import DemographicSystem from adcp.types.generated_poc.enums.device_platform import DevicePlatform +from adcp.types.generated_poc.enums.device_type import DeviceType +from adcp.types.generated_poc.enums.digital_source_type import DigitalSourceType from adcp.types.generated_poc.enums.dimension_unit import DimensionUnit +from adcp.types.generated_poc.enums.disclosure_position import DisclosurePosition from adcp.types.generated_poc.enums.distance_unit import DistanceUnit +from adcp.types.generated_poc.enums.error_code import ErrorCode from adcp.types.generated_poc.enums.event_type import EventType from adcp.types.generated_poc.enums.feed_format import FeedFormat from adcp.types.generated_poc.enums.feedback_source import FeedbackSource @@ -75,6 +81,7 @@ from adcp.types.generated_poc.enums.signal_source import SignalSource from adcp.types.generated_poc.enums.signal_value_type import SignalValueType from adcp.types.generated_poc.enums.sort_direction import SortDirection +from adcp.types.generated_poc.enums.sort_metric import SortMetric from adcp.types.generated_poc.enums.task_status import TaskStatus from adcp.types.generated_poc.enums.task_type import TaskType from adcp.types.generated_poc.enums.transport_mode import TransportMode @@ -90,14 +97,32 @@ from adcp.types.generated_poc.enums.webhook_security_method import WebhookSecurityMethod from adcp.types.generated_poc.a2ui.component import A2UiComponent from adcp.types.generated_poc.a2ui.surface import A2UiSurface -from adcp.types.generated_poc.account.list_accounts_request import ListAccountsRequest, Status +from adcp.types.generated_poc.account.get_account_financials_request import ( + GetAccountFinancialsRequest, +) +from adcp.types.generated_poc.account.get_account_financials_response import ( + Balance, + Credit, + GetAccountFinancialsResponse, + GetAccountFinancialsResponse1, + GetAccountFinancialsResponse2, + Invoice, + LastTopUp, + PaymentStatus, + Spend, + Status, +) +from adcp.types.generated_poc.account.list_accounts_request import ListAccountsRequest from adcp.types.generated_poc.account.list_accounts_response import ListAccountsResponse +from adcp.types.generated_poc.account.report_usage_request import ReportUsageRequest, UsageItem +from adcp.types.generated_poc.account.report_usage_response import ReportUsageResponse from adcp.types.generated_poc.account.sync_accounts_request import ( Account, Billing, SyncAccountsRequest, ) from adcp.types.generated_poc.account.sync_accounts_response import ( + AccountScope, Action, CreditLimit, Setup, @@ -243,6 +268,8 @@ ) from adcp.types.generated_poc.content_standards.update_content_standards_response import ( UpdateContentStandardsResponse, + UpdateContentStandardsResponse1, + UpdateContentStandardsResponse2, ) from adcp.types.generated_poc.content_standards.validate_content_delivery_request import ( Record, @@ -255,12 +282,19 @@ ValidateContentDeliveryResponse1, ValidateContentDeliveryResponse2, ) +from adcp.types.generated_poc.core.account_ref import ( + AccountReference, + AccountReference1, + AccountReference2, +) from adcp.types.generated_poc.core.activation_key import ( ActivationKey, ActivationKey1, ActivationKey2, ) from adcp.types.generated_poc.core.assets.audio_asset import AudioAsset, BitDepth, Channels +from adcp.types.generated_poc.core.assets.brief_asset import BriefAsset +from adcp.types.generated_poc.core.assets.catalog_asset import CatalogAsset from adcp.types.generated_poc.core.assets.css_asset import CssAsset from adcp.types.generated_poc.core.assets.daast_asset import DaastAsset, DaastAsset1, DaastAsset2 from adcp.types.generated_poc.core.assets.html_asset import Accessibility, HtmlAsset @@ -295,6 +329,7 @@ ) from adcp.types.generated_poc.core.brand_ref import BrandReference from adcp.types.generated_poc.core.catalog import Catalog, Gtin +from adcp.types.generated_poc.core.catalog_field_mapping import CatalogFieldMapping, Transform from adcp.types.generated_poc.core.catchment import ( Catchment, Catchment1, @@ -312,8 +347,14 @@ from adcp.types.generated_poc.core.context import ContextObject from adcp.types.generated_poc.core.creative_asset import CreativeAsset, Input from adcp.types.generated_poc.core.creative_assignment import CreativeAssignment -from adcp.types.generated_poc.core.creative_brief import CreativeBrief, Messaging, Objective -from adcp.types.generated_poc.core.creative_brief_ref import CreativeBriefReference +from adcp.types.generated_poc.core.creative_brief import ( + Compliance, + CreativeBrief, + Jurisdiction, + Messaging, + Objective, + RequiredDisclosure, +) from adcp.types.generated_poc.core.creative_filters import CreativeFilters from adcp.types.generated_poc.core.creative_manifest import CreativeManifest from adcp.types.generated_poc.core.creative_policy import CreativePolicy @@ -324,6 +365,8 @@ DataProviderSignalSelector2, DataProviderSignalSelector3, ) +from adcp.types.generated_poc.core.date_range import DateRange +from adcp.types.generated_poc.core.datetime_range import DatetimeRange from adcp.types.generated_poc.core.daypart_target import DaypartTarget from adcp.types.generated_poc.core.delivery_forecast import DeliveryForecast from adcp.types.generated_poc.core.delivery_metrics import ( @@ -343,13 +386,14 @@ DestinationType, Location, ) +from adcp.types.generated_poc.core.duration import Duration from adcp.types.generated_poc.core.education_item import DegreeType, EducationItem, Level, Modality -from adcp.types.generated_poc.core.error import Error +from adcp.types.generated_poc.core.error import Error, Recovery from adcp.types.generated_poc.core.event import Event from adcp.types.generated_poc.core.event_custom_data import Content, EventCustomData from adcp.types.generated_poc.core.ext import ExtensionObject from adcp.types.generated_poc.core.flight_item import FlightItem, Origin -from adcp.types.generated_poc.core.forecast_point import ForecastPoint +from adcp.types.generated_poc.core.forecast_point import ForecastPoint, Metrics from adcp.types.generated_poc.core.forecast_range import ForecastRange from adcp.types.generated_poc.core.format import ( Assets10, @@ -371,6 +415,8 @@ Assets26, Assets27, Assets28, + Assets29, + Assets30, Assets5, Assets6, Assets7, @@ -389,7 +435,13 @@ SelectionMode, ) from adcp.types.generated_poc.core.format_id import FormatId -from adcp.types.generated_poc.core.frequency_cap import FrequencyCap +from adcp.types.generated_poc.core.frequency_cap import ( + FrequencyCap, + FrequencyCap1, + FrequencyCap2, + FrequencyCap3, +) +from adcp.types.generated_poc.core.geo_breakdown_support import GeographicBreakdownSupport from adcp.types.generated_poc.core.hotel_item import Address, HotelItem from adcp.types.generated_poc.core.identifier import Identifier from adcp.types.generated_poc.core.job_item import ( @@ -400,12 +452,26 @@ Salary, ) from adcp.types.generated_poc.core.mcp_webhook_payload import McpWebhookPayload -from adcp.types.generated_poc.core.measurement import Measurement from adcp.types.generated_poc.core.media_buy import MediaBuy from adcp.types.generated_poc.core.media_buy_features import MediaBuyFeatures from adcp.types.generated_poc.core.offering import GeoTargets, Metro, Offering, PostalArea, Region from adcp.types.generated_poc.core.offering_asset_group import OfferingAssetGroup -from adcp.types.generated_poc.core.optimization_goal import OptimizationGoal +from adcp.types.generated_poc.core.optimization_goal import ( + EventSource, + Metric, + OptimizationGoal, + OptimizationGoal1, + OptimizationGoal2, + Target, + Target1, + Target2, + Target3, + Target4, + TargetFrequency, + TargetFrequency1, +) +from adcp.types.generated_poc.core.outcome_measurement import OutcomeMeasurement +from adcp.types.generated_poc.core.overlay import Bounds, Overlay, Visual from adcp.types.generated_poc.core.pagination_request import PaginationRequest from adcp.types.generated_poc.core.pagination_response import PaginationResponse from adcp.types.generated_poc.core.performance_feedback import ( @@ -420,10 +486,14 @@ ConversionTracking, DeliveryMeasurement, MatchedGtin, + MetricOptimization, Product, ProductCard, ProductCardDetailed, - SupportedOptimizationStrategy, + SupportedMetric, + SupportedTarget, + SupportedTarget1, + SupportedViewDuration, ) from adcp.types.generated_poc.core.product_allocation import ProductAllocation from adcp.types.generated_poc.core.product_filters import ( @@ -437,6 +507,15 @@ from adcp.types.generated_poc.core.property_tag import PropertyTag from adcp.types.generated_poc.core.proposal import Proposal, TotalBudgetGuidance from adcp.types.generated_poc.core.protocol_envelope import ProtocolEnvelope +from adcp.types.generated_poc.core.provenance import ( + AiTool, + C2pa, + DeclaredBy, + Disclosure, + HumanOversight, + Provenance, + VerificationItem, +) from adcp.types.generated_poc.core.publisher_property_selector import ( PublisherPropertySelector, PublisherPropertySelector1, @@ -460,6 +539,12 @@ Channel, SampleRate, ) +from adcp.types.generated_poc.core.requirements.catalog_field_binding import ( + AssetPoolBinding, + CatalogFieldBinding, + CatalogFieldBinding1, + ScalarBinding, +) from adcp.types.generated_poc.core.requirements.catalog_requirements import CatalogRequirements from adcp.types.generated_poc.core.requirements.css_asset_requirements import CssAssetRequirements from adcp.types.generated_poc.core.requirements.daast_asset_requirements import ( @@ -501,6 +586,22 @@ from adcp.types.generated_poc.core.signal_definition import Range, SignalDefinition, Tag from adcp.types.generated_poc.core.signal_filters import SignalFilters from adcp.types.generated_poc.core.signal_id import SignalId2, SignalId3 +from adcp.types.generated_poc.core.signal_pricing import ( + SignalPricing, + SignalPricing1, + SignalPricing2, + SignalPricing3, +) +from adcp.types.generated_poc.core.signal_pricing_option import ( + SignalPricingOption, + SignalPricingOption1, + SignalPricingOption2, + SignalPricingOption3, + SignalPricingOption4, + SignalPricingOption5, + SignalPricingOption6, + SignalPricingOption7, +) from adcp.types.generated_poc.core.signal_targeting import ( SignalTargeting, SignalTargeting1, @@ -518,11 +619,21 @@ GeoMetrosExcludeItem, GeoPostalArea, GeoPostalAreasExcludeItem, + GeoProximity, + GeoProximity2, + GeoProximity3, GeoRegion, GeoRegionsExcludeItem, + Geometry4, + Geometry5, + KeywordTarget, LanguageItem, + MatchType, + NegativeKeyword, StoreCatchment, TargetingOverlay, + TravelTime4, + TravelTime5, ) from adcp.types.generated_poc.core.user_match import ( UserMatch, @@ -628,15 +739,24 @@ CreateMediaBuyResponse2, ) from adcp.types.generated_poc.media_buy.get_media_buy_delivery_request import ( + Geo, GetMediaBuyDeliveryRequest, + ReportingDimensions, StatusFilter, ) from adcp.types.generated_poc.media_buy.get_media_buy_delivery_response import ( AggregatedTotals, + ByAudienceItem, ByCatalogItemItem, ByCreativeItem, + ByDevicePlatformItem, + ByDeviceTypeItem, + ByGeoItem, + ByKeywordItem, ByPackageItem, + ByPlacementItem, DailyBreakdownItem, + DailyBreakdownItem1, GetMediaBuyDeliveryResponse, MediaBuyDelivery, Totals, @@ -657,8 +777,22 @@ from adcp.types.generated_poc.media_buy.get_products_async_response_working import ( GetProductsWorking, ) -from adcp.types.generated_poc.media_buy.get_products_request import GetProductsRequest -from adcp.types.generated_poc.media_buy.get_products_response import GetProductsResponse +from adcp.types.generated_poc.media_buy.get_products_request import ( + Action2, + BuyingMode, + FieldModel, + GetProductsRequest, + GetProductsRequest1, + GetProductsRequest2, + GetProductsRequest3, + Refine, + Refine1, + Refine2, +) +from adcp.types.generated_poc.media_buy.get_products_response import ( + GetProductsResponse, + RefinementAppliedItem, +) from adcp.types.generated_poc.media_buy.list_creative_formats_request import ( ListCreativeFormatsRequest, ) @@ -666,7 +800,7 @@ ListCreativeFormatsResponse, ) from adcp.types.generated_poc.media_buy.list_creatives_request import ( - FieldModel, + Field1, ListCreativesRequest, Sort, ) @@ -688,6 +822,14 @@ ) from adcp.types.generated_poc.media_buy.package_request import PackageRequest from adcp.types.generated_poc.media_buy.package_update import ( + KeywordTargetsAddItem, + KeywordTargetsAddItem1, + KeywordTargetsRemoveItem, + KeywordTargetsRemoveItem1, + NegativeKeywordsAddItem, + NegativeKeywordsAddItem1, + NegativeKeywordsRemoveItem, + NegativeKeywordsRemoveItem1, PackageUpdate, PackageUpdate1, PackageUpdate2, @@ -703,8 +845,7 @@ ProvidePerformanceFeedbackResponse2, ) from adcp.types.generated_poc.media_buy.sync_audiences_request import ( - Audience, - ConsentBasis, + AudienceType, SyncAudiencesRequest, ) from adcp.types.generated_poc.media_buy.sync_audiences_response import ( @@ -737,16 +878,16 @@ from adcp.types.generated_poc.media_buy.sync_creatives_async_response_working import ( SyncCreativesWorking, ) -from adcp.types.generated_poc.media_buy.sync_creatives_request import SyncCreativesRequest +from adcp.types.generated_poc.media_buy.sync_creatives_request import ( + Assignment, + SyncCreativesRequest, +) from adcp.types.generated_poc.media_buy.sync_creatives_response import ( SyncCreativesResponse, SyncCreativesResponse1, SyncCreativesResponse2, ) -from adcp.types.generated_poc.media_buy.sync_event_sources_request import ( - EventSource, - SyncEventSourcesRequest, -) +from adcp.types.generated_poc.media_buy.sync_event_sources_request import SyncEventSourcesRequest from adcp.types.generated_poc.media_buy.sync_event_sources_response import ( ManagedBy, SnippetType, @@ -828,12 +969,13 @@ GetAdcpCapabilitiesRequest, ) from adcp.types.generated_poc.protocol.get_adcp_capabilities_response import ( + AccountResolution, Adcp, AudienceTargeting, + AvailableDimension, CreativeFeature, CreativeSpecs, DataProviderDomain, - DefaultBilling, Endpoint, Execution, ExtensionsSupportedItem, @@ -842,16 +984,21 @@ GeoPostalAreas, GetAdcpCapabilitiesResponse, Governance, + KeywordTargets, MajorVersion, MatchingLatencyHours, MraidVersion, + NegativeKeywords, Portfolio, Preferred, PrimaryCountry, PublisherDomain, + Reporting, Signals, SponsoredIntelligence, + SupportedBillingEnum, SupportedIdentifierType, + SupportedMatchType, SupportedProtocol, Targeting, Transport, @@ -863,17 +1010,11 @@ ActivateSignalResponse2, ) from adcp.types.generated_poc.signals.get_signals_request import ( - DeliverTo, - DeliverTo1, GetSignalsRequest, GetSignalsRequest1, GetSignalsRequest2, ) -from adcp.types.generated_poc.signals.get_signals_response import ( - GetSignalsResponse, - Pricing, - Signal, -) +from adcp.types.generated_poc.signals.get_signals_response import GetSignalsResponse, Signal from adcp.types.generated_poc.sponsored_intelligence.si_capabilities import ( A2ui, Commerce, @@ -939,6 +1080,15 @@ from adcp.types.generated_poc.media_buy.get_media_buys_response import ( DeliveryStatus as _DeliveryStatusFromGetMediaBuysResponse, ) +from adcp.types.generated_poc.media_buy.get_media_buy_delivery_request import ( + Audience as _AudienceFromGetMediaBuyDeliveryRequest, +) +from adcp.types.generated_poc.media_buy.sync_audiences_request import ( + Audience as _AudienceFromSyncAudiencesRequest, +) +from adcp.types.generated_poc.media_buy.sync_audiences_response import ( + Audience as _AudienceFromSyncAudiencesResponse, +) # Backward compatibility aliases for renamed types MediaBuyPackage = _PackageFromGetMediaBuysResponse @@ -982,6 +1132,47 @@ class AssetSelectors(_AdCPBaseModel): model_config = _ConfigDict(extra="allow") +# DeliverTo was removed in upstream schemas (v2.5.x). The GetSignalsRequest wire +# format changed: 'deliver_to' nested object with 'deployments'+'countries' was +# replaced by top-level 'destinations' and 'countries' fields. Code using +# GetSignalsRequest(deliver_to=DeliverTo(...)) must migrate to the new field names. +# This stub prevents ImportError only; it cannot preserve the old wire behavior. +class DeliverTo(_AdCPBaseModel): + model_config = _ConfigDict(extra="allow") + + +class DeliverTo1(DeliverTo): + pass + + +# Pricing was removed in upstream schemas (v2.5.x). Replaced by SignalPricingOption +# which has the same cpm/currency fields. This stub preserves imports only. +class Pricing(_AdCPBaseModel): + model_config = _ConfigDict(extra="allow") + + +# Measurement was renamed to OutcomeMeasurement upstream. This alias preserves imports. +Measurement = OutcomeMeasurement + + +# ListAuthorizedProperties was removed from upstream schemas. +# Replaced by adagents.json-based authorization model. +class ListAuthorizedPropertiesRequest(_AdCPBaseModel): + model_config = _ConfigDict(extra="allow") + + +class ListAuthorizedPropertiesResponse(_AdCPBaseModel): + model_config = _ConfigDict(extra="allow") + + +# PackageStatus enum was removed from upstream schemas. +class PackageStatus(str, _Enum): + draft = "draft" + active = "active" + paused = "paused" + completed = "completed" + + # Explicit exports __all__ = [ "A2UiComponent", @@ -989,8 +1180,14 @@ class AssetSelectors(_AdCPBaseModel): "A2ui", "Accessibility", "Account", + "AccountReference", + "AccountReference1", + "AccountReference2", + "AccountResolution", + "AccountScope", "AcpHandoff", "Action", + "Action2", "ActionResponse", "ActionSource", "ActivateSignalRequest", @@ -1012,6 +1209,7 @@ class AssetSelectors(_AdCPBaseModel): "AgeVerificationMethod", "AgenticCheckout", "AggregatedTotals", + "AiTool", "Architecture", "Area", "Artifact", @@ -1023,6 +1221,7 @@ class AssetSelectors(_AdCPBaseModel): "AssetAccess2", "AssetAccess3", "AssetContentType", + "AssetPoolBinding", "AssetRequirements", "AssetSelectors", "AssetType", @@ -1048,22 +1247,26 @@ class AssetSelectors(_AdCPBaseModel): "Assets26", "Assets27", "Assets28", + "Assets29", "Assets3", + "Assets30", "Assets5", "Assets6", "Assets7", "Assets8", "Assets9", "AssignedPackage", + "Assignment", "Assignments", "AttributionModel", "AttributionWindow", - "Audience", "AudienceMember", "AudienceMember1", "AudienceMember2", "AudienceMember3", + "AudienceSource", "AudienceTargeting", + "AudienceType", "AudioAsset", "AudioAssetRequirements", "AudioBitDepth", @@ -1077,9 +1280,11 @@ class AssetSelectors(_AdCPBaseModel): "AuthorizedAgents4", "AuthorizedAgents5", "AuthorizedOperator", + "AvailableDimension", "AvailableMetric", "Avatar", "Background", + "Balance", "BaseGroupAsset", "BaseIndividualAsset", "BasePropertySource", @@ -1090,6 +1295,7 @@ class AssetSelectors(_AdCPBaseModel): "Billing", "BitDepth", "BodyStyle", + "Bounds", "Brand", "Brand1", "BrandAgent", @@ -1102,17 +1308,26 @@ class AssetSelectors(_AdCPBaseModel): "BrandId", "BrandManifest", "BrandReference", + "BriefAsset", "BudgetRange", "BudgetRange1", "BuildCreativeRequest", "BuildCreativeResponse", "BuildCreativeResponse1", "BuildCreativeResponse2", + "BuyingMode", "ByActionSourceItem", + "ByAudienceItem", "ByCatalogItemItem", "ByCreativeItem", + "ByDevicePlatformItem", + "ByDeviceTypeItem", "ByEventTypeItem", + "ByGeoItem", + "ByKeywordItem", "ByPackageItem", + "ByPlacementItem", + "C2pa", "CalibrateContentRequest", "CalibrateContentResponse", "CalibrateContentResponse1", @@ -1120,6 +1335,10 @@ class AssetSelectors(_AdCPBaseModel): "CalibrationExemplars", "Catalog", "CatalogAction", + "CatalogAsset", + "CatalogFieldBinding", + "CatalogFieldBinding1", + "CatalogFieldMapping", "CatalogItemStatus", "CatalogMatch", "CatalogRequirements", @@ -1142,6 +1361,7 @@ class AssetSelectors(_AdCPBaseModel): "ColorValue2Item", "Colors", "Commerce", + "Compliance", "Components", "Condition", "ConsentBasis", @@ -1186,7 +1406,6 @@ class AssetSelectors(_AdCPBaseModel): "CreativeAsset", "CreativeAssignment", "CreativeBrief", - "CreativeBriefReference", "CreativeFeature", "CreativeFeatureResult", "CreativeFilters", @@ -1196,6 +1415,7 @@ class AssetSelectors(_AdCPBaseModel): "CreativeSpecs", "CreativeStatus", "CreativeVariant", + "Credit", "CreditLimit", "CssAsset", "CssAssetRequirements", @@ -1206,15 +1426,18 @@ class AssetSelectors(_AdCPBaseModel): "DaastTrackingEvent", "DaastVersion", "DailyBreakdownItem", + "DailyBreakdownItem1", "DataProviderDomain", "DataProviderSignalSelector", "DataProviderSignalSelector1", "DataProviderSignalSelector2", "DataProviderSignalSelector3", + "DateRange", "DateRangeSupport", + "DatetimeRange", "DayOfWeek", "DaypartTarget", - "DefaultBilling", + "DeclaredBy", "DegreeType", "DeletePropertyListRequest", "DeletePropertyListResponse", @@ -1236,18 +1459,24 @@ class AssetSelectors(_AdCPBaseModel): "DestinationItem", "DestinationType", "DevicePlatform", + "DeviceType", + "DigitalSourceType", "DimensionUnit", "Dimensions", "Dimensions1", "Disclaimer", + "Disclosure", + "DisclosurePosition", "DistanceUnit", "Domain", "DoohMetrics", + "Duration", "EducationItem", "Embedding", "EmploymentType", "Endpoint", "Error", + "ErrorCode", "Event", "EventCustomData", "EventSource", @@ -1262,6 +1491,7 @@ class AssetSelectors(_AdCPBaseModel): "Features", "FeedFormat", "FeedbackSource", + "Field1", "FieldModel", "FlatRatePricingOption", "FlightItem", @@ -1281,9 +1511,13 @@ class AssetSelectors(_AdCPBaseModel): "FrameRate", "FrameRateType", "FrequencyCap", + "FrequencyCap1", + "FrequencyCap2", + "FrequencyCap3", "FrequencyCapScope", "FuelType", "GenerationContext", + "Geo", "GeoCountriesExcludeItem", "GeoCountry", "GeoMetro", @@ -1292,13 +1526,23 @@ class AssetSelectors(_AdCPBaseModel): "GeoPostalArea", "GeoPostalAreas", "GeoPostalAreasExcludeItem", + "GeoProximity", + "GeoProximity2", + "GeoProximity3", "GeoRegion", "GeoRegionsExcludeItem", "GeoTargets", + "GeographicBreakdownSupport", "GeographicTargetingLevel", "Geometry", "Geometry1", "Geometry2", + "Geometry4", + "Geometry5", + "GetAccountFinancialsRequest", + "GetAccountFinancialsResponse", + "GetAccountFinancialsResponse1", + "GetAccountFinancialsResponse2", "GetAdcpCapabilitiesRequest", "GetAdcpCapabilitiesResponse", "GetContentStandardsRequest", @@ -1324,6 +1568,9 @@ class AssetSelectors(_AdCPBaseModel): "GetMediaBuysResponse", "GetProductsInputRequired", "GetProductsRequest", + "GetProductsRequest1", + "GetProductsRequest2", + "GetProductsRequest3", "GetProductsResponse", "GetProductsSubmitted", "GetProductsWorking", @@ -1344,6 +1591,7 @@ class AssetSelectors(_AdCPBaseModel): "HtmlAsset", "HtmlAssetRequirements", "HttpMethod", + "HumanOversight", "Identifier", "Identifiers", "IfNotCovered", @@ -1353,17 +1601,28 @@ class AssetSelectors(_AdCPBaseModel): "Input2", "Input4", "Intent", + "Invoice", "ItemIssue", "JavascriptAsset", "JavascriptAssetRequirements", "JavascriptModuleType", "JobItem", + "Jurisdiction", "KellerType", + "KeywordTarget", + "KeywordTargets", + "KeywordTargetsAddItem", + "KeywordTargetsAddItem1", + "KeywordTargetsRemoveItem", + "KeywordTargetsRemoveItem1", "LandingPageRequirement", "LanguageItem", + "LastTopUp", "Level", "ListAccountsRequest", "ListAccountsResponse", + "ListAuthorizedPropertiesRequest", + "ListAuthorizedPropertiesResponse", "ListContentStandardsRequest", "ListContentStandardsResponse", "ListContentStandardsResponse1", @@ -1390,6 +1649,7 @@ class AssetSelectors(_AdCPBaseModel): "MarkdownAsset", "MarkdownAssetRequirements", "MarkdownFlavor", + "MatchType", "MatchedGtin", "MatchingLatencyHours", "MatchingProduct", @@ -1405,7 +1665,10 @@ class AssetSelectors(_AdCPBaseModel): "Messaging", "Metadata", "Method", + "Metric", + "MetricOptimization", "MetricType", + "Metrics", "Metro", "MetroAreaSystem", "Mileage", @@ -1414,16 +1677,27 @@ class AssetSelectors(_AdCPBaseModel): "ModuleType", "MoovAtomPosition", "MraidVersion", + "NegativeKeyword", + "NegativeKeywords", + "NegativeKeywordsAddItem", + "NegativeKeywordsAddItem1", + "NegativeKeywordsRemoveItem", + "NegativeKeywordsRemoveItem1", "NotificationType", "Objective", "Offering", "OfferingAssetConstraint", "OfferingAssetGroup", "OptimizationGoal", + "OptimizationGoal1", + "OptimizationGoal2", "Orientation", "Origin", + "OutcomeMeasurement", + "Overlay", "Pacing", "PackageRequest", + "PackageStatus", "PackageUpdate", "PackageUpdate1", "PackageUpdate2", @@ -1433,6 +1707,7 @@ class AssetSelectors(_AdCPBaseModel): "Parameters", "PartialFailure", "Pass", + "PaymentStatus", "Performance", "PerformanceFeedback", "Period", @@ -1491,6 +1766,7 @@ class AssetSelectors(_AdCPBaseModel): "Protocol", "ProtocolEnvelope", "ProtocolResponse", + "Provenance", "ProvidePerformanceFeedbackRequest", "ProvidePerformanceFeedbackRequest1", "ProvidePerformanceFeedbackRequest2", @@ -1513,15 +1789,25 @@ class AssetSelectors(_AdCPBaseModel): "RealEstateItem", "Reason", "Record", + "Recovery", "ReferenceAsset", + "Refine", + "Refine1", + "Refine2", + "RefinementAppliedItem", "Region", "Renders", "Renders1", + "ReportUsageRequest", + "ReportUsageResponse", + "Reporting", "ReportingCapabilities", + "ReportingDimensions", "ReportingFrequency", "ReportingPeriod", "ReportingWebhook", "Request", + "RequiredDisclosure", "RequiredGeoTargetingItem", "Response", "Response1", @@ -1535,6 +1821,7 @@ class AssetSelectors(_AdCPBaseModel): "Sampling", "SamplingInfo", "Sandbox", + "ScalarBinding", "ScanType", "Scope", "Security", @@ -1562,6 +1849,18 @@ class AssetSelectors(_AdCPBaseModel): "SignalId", "SignalId2", "SignalId3", + "SignalPricing", + "SignalPricing1", + "SignalPricing2", + "SignalPricing3", + "SignalPricingOption", + "SignalPricingOption1", + "SignalPricingOption2", + "SignalPricingOption3", + "SignalPricingOption4", + "SignalPricingOption5", + "SignalPricingOption6", + "SignalPricingOption7", "SignalSource", "SignalTag", "SignalTags", @@ -1577,6 +1876,8 @@ class AssetSelectors(_AdCPBaseModel): "Sort", "SortApplied", "SortDirection", + "SortMetric", + "Spend", "SponsoredIntelligence", "Standard", "StandardEnum", @@ -1591,9 +1892,14 @@ class AssetSelectors(_AdCPBaseModel): "SubAsset1", "SubAsset2", "Summary", + "SupportedBillingEnum", "SupportedIdentifierType", - "SupportedOptimizationStrategy", + "SupportedMatchType", + "SupportedMetric", "SupportedProtocol", + "SupportedTarget", + "SupportedTarget1", + "SupportedViewDuration", "SyncAccountsRequest", "SyncAccountsResponse", "SyncAccountsResponse1", @@ -1622,6 +1928,13 @@ class AssetSelectors(_AdCPBaseModel): "SyncEventSourcesResponse2", "Tag", "Tags", + "Target", + "Target1", + "Target2", + "Target3", + "Target4", + "TargetFrequency", + "TargetFrequency1", "Targeting", "TargetingOverlay", "TaskStatus", @@ -1640,12 +1953,15 @@ class AssetSelectors(_AdCPBaseModel): "TransactionIntent", "TranscriptSource", "TranscriptSource1", + "Transform", "Transmission", "Transport", "TransportMode", "TravelTime", "TravelTime1", "TravelTime2", + "TravelTime4", + "TravelTime5", "Type", "Uid", "UidType", @@ -1653,6 +1969,8 @@ class AssetSelectors(_AdCPBaseModel): "UniversalMacro", "UpdateContentStandardsRequest", "UpdateContentStandardsResponse", + "UpdateContentStandardsResponse1", + "UpdateContentStandardsResponse2", "UpdateFrequency", "UpdateMediaBuyInputRequired", "UpdateMediaBuyRequest", @@ -1668,6 +1986,7 @@ class AssetSelectors(_AdCPBaseModel): "UrlAsset", "UrlAssetRequirements", "UrlAssetType", + "UsageItem", "User", "UserMatch", "UserMatch1", @@ -1691,6 +2010,7 @@ class AssetSelectors(_AdCPBaseModel): "VehicleItem", "VenueBreakdownItem", "Verdict", + "VerificationItem", "Video", "VideoAsset", "VideoAssetRequirements", @@ -1698,12 +2018,16 @@ class AssetSelectors(_AdCPBaseModel): "ViewThreshold", "ViewThreshold1", "Viewability", + "Visual", "Voice", "WcagLevel", "WebhookAsset", "WebhookAssetRequirements", "WebhookResponseType", "WebhookSecurityMethod", + "_AudienceFromGetMediaBuyDeliveryRequest", + "_AudienceFromSyncAudiencesRequest", + "_AudienceFromSyncAudiencesResponse", "_DeliveryStatusFromGetMediaBuyDeliveryResponse", "_PackageFromPackage", ] diff --git a/src/adcp/types/aliases.py b/src/adcp/types/aliases.py index 47c0c95d..20620347 100644 --- a/src/adcp/types/aliases.py +++ b/src/adcp/types/aliases.py @@ -32,20 +32,30 @@ from __future__ import annotations from adcp.types._generated import ( + # Account reference variants + AccountReference1, + AccountReference2, # Activation responses ActivateSignalResponse1, ActivateSignalResponse2, + # Activation key variants + ActivationKey1, + ActivationKey2, # Authorized agents AuthorizedAgents, AuthorizedAgents1, AuthorizedAgents2, AuthorizedAgents3, + AuthorizedAgents4, + AuthorizedAgents5, # Build creative responses BuildCreativeResponse1, BuildCreativeResponse2, # Calibrate content responses CalibrateContentResponse1, CalibrateContentResponse2, + ConsentBasis, + CpaPricingOption, CpcPricingOption, CpcvPricingOption, CpmPricingOption, @@ -67,6 +77,9 @@ Destination1, Destination2, FlatRatePricingOption, + # Get account financials responses + GetAccountFinancialsResponse1, + GetAccountFinancialsResponse2, # Content standards get responses GetContentStandardsResponse1, GetContentStandardsResponse2, @@ -74,9 +87,19 @@ GetCreativeDeliveryRequest1, GetCreativeDeliveryRequest2, GetCreativeDeliveryRequest3, + # Get creative features responses + GetCreativeFeaturesResponse1, + GetCreativeFeaturesResponse2, # Media buy artifacts responses GetMediaBuyArtifactsResponse1, GetMediaBuyArtifactsResponse2, + # Get products request variants + GetProductsRequest1, + GetProductsRequest2, + GetProductsRequest3, + # Get signals request variants + GetSignalsRequest1, + GetSignalsRequest2, # Content standards list responses ListContentStandardsResponse1, ListContentStandardsResponse2, @@ -86,9 +109,11 @@ # Preview creative requests PreviewCreativeRequest1, PreviewCreativeRequest2, + PreviewCreativeRequest3, # Preview creative responses PreviewCreativeResponse1, PreviewCreativeResponse2, + PreviewCreativeResponse3, # Preview renders (discriminated union by output_format) PreviewRender1, # output_format='url' PreviewRender2, # output_format='html' @@ -96,15 +121,28 @@ # Publisher properties types PropertyId, PropertyTag, + # Performance feedback requests + ProvidePerformanceFeedbackRequest1, + ProvidePerformanceFeedbackRequest2, # Performance feedback responses ProvidePerformanceFeedbackResponse1, ProvidePerformanceFeedbackResponse2, + # Signal pricing option variants + SignalPricingOption5, + SignalPricingOption6, + SignalPricingOption7, + # SI send message request variants + SiSendMessageRequest1, + SiSendMessageRequest2, # SubAssets SubAsset1, SubAsset2, # Sync accounts responses SyncAccountsResponse1, SyncAccountsResponse2, + # Sync audiences responses + SyncAudiencesResponse1, + SyncAudiencesResponse2, # Sync catalogs responses SyncCatalogsResponse1, SyncCatalogsResponse2, @@ -114,6 +152,10 @@ # Sync event sources responses SyncEventSourcesResponse1, SyncEventSourcesResponse2, + TimeBasedPricingOption, + # Update content standards responses + UpdateContentStandardsResponse1, + UpdateContentStandardsResponse2, # Update media buy requests UpdateMediaBuyRequest1, UpdateMediaBuyRequest2, @@ -128,6 +170,9 @@ VastAsset2, VcpmPricingOption, ) + +# CatalogFieldBinding1 = catalog_group binding; give it a semantic name. +from adcp.types._generated import CatalogFieldBinding1 as CatalogGroupBinding from adcp.types._generated import ( PublisherPropertySelector1 as PublisherPropertiesInternal, ) @@ -144,6 +189,17 @@ # Import Package from _generated (still uses qualified name for internal reasons) from adcp.types._generated import _PackageFromPackage as Package +# Status name collides across many modules. Preserve backward compat by importing +# the specific variant that was exported on main (media buy delivery status). +from adcp.types.generated_poc.media_buy.get_media_buy_delivery_response import ( + Status as MediaBuyDeliveryStatus, +) + +# Audience name collides in _generated (delivery breakdown wins over sync request) +from adcp.types.generated_poc.media_buy.sync_audiences_request import ( + Audience as SyncAudiencesAudienceInternal, +) + # Import nested types that aren't exported by _generated but are useful for type hints from adcp.types.generated_poc.media_buy.sync_catalogs_response import ( Catalog as SyncCatalogResultInternal, @@ -152,6 +208,57 @@ Creative as SyncCreativeResultInternal, ) +# ============================================================================ +# ACCOUNT REFERENCE ALIASES - Identification Method Discriminated Unions +# ============================================================================ +# AccountReference is a discriminated union with two identification methods: +# +# 1. By seller-assigned ID (account_id): +# - Use when the buyer manages accounts via list_accounts or sync_accounts +# - Requires seller-assigned account_id string +# +# 2. By natural key (brand + operator): +# - Use when the seller resolves accounts internally from brand identity +# - Requires brand reference + operator domain + +AccountReferenceById = AccountReference1 +"""Account reference using a seller-assigned account ID. + +Use when the buyer manages accounts (e.g., picked from list_accounts or +sync_accounts). The account_id must match one returned by the seller. + +Fields: +- account_id: Seller-assigned account identifier + +Example: + ```python + from adcp import AccountReferenceById + + account = AccountReferenceById(account_id="acc_acme_001") + ``` +""" + +AccountReferenceByNaturalKey = AccountReference2 +"""Account reference using brand + operator natural key. + +Use when the seller resolves accounts internally from brand identity. +The seller looks up the account based on the brand/operator combination. + +Fields: +- brand: BrandReference identifying the advertiser +- operator: Domain of the entity operating on the brand's behalf + +Example: + ```python + from adcp import AccountReferenceByNaturalKey + + account = AccountReferenceByNaturalKey( + brand={"domain": "acme-corp.com"}, + operator="acme-corp.com" + ) + ``` +""" + # ============================================================================ # RESPONSE TYPE ALIASES - Success/Error Discriminated Unions # ============================================================================ @@ -291,6 +398,13 @@ def process_result(result: SyncCatalogResult) -> None: CreateContentStandardsErrorResponse = CreateContentStandardsResponse2 """Error response - content standards creation failed.""" +# Update Content Standards Response Variants +UpdateContentStandardsSuccessResponse = UpdateContentStandardsResponse1 +"""Success response - content standards updated, returns standards_id.""" + +UpdateContentStandardsErrorResponse = UpdateContentStandardsResponse2 +"""Error response - content standards update failed, includes errors.""" + # Get Media Buy Artifacts Response Variants GetMediaBuyArtifactsSuccessResponse = GetMediaBuyArtifactsResponse1 """Success response - media buy artifacts retrieved.""" @@ -305,16 +419,67 @@ def process_result(result: SyncCatalogResult) -> None: UpdateMediaBuyErrorResponse = UpdateMediaBuyResponse2 """Error response - media buy update failed, no changes applied.""" +# Get Account Financials Response Variants +GetAccountFinancialsSuccessResponse = GetAccountFinancialsResponse1 +"""Success response - account financials retrieved.""" + +GetAccountFinancialsErrorResponse = GetAccountFinancialsResponse2 +"""Error response - account financials retrieval failed.""" + +# Sync Audiences Response Variants +SyncAudiencesSuccessResponse = SyncAudiencesResponse1 +"""Success response - audiences synced successfully.""" + +SyncAudiencesErrorResponse = SyncAudiencesResponse2 +"""Error response - audiences sync failed.""" + +# Get Creative Features Response Variants +GetCreativeFeaturesSuccessResponse = GetCreativeFeaturesResponse1 +"""Success response - creative features retrieved.""" + +GetCreativeFeaturesErrorResponse = GetCreativeFeaturesResponse2 +"""Error response - creative features retrieval failed.""" + # ============================================================================ # REQUEST TYPE ALIASES - Operation Variants # ============================================================================ # Preview Creative Request Variants -PreviewCreativeFormatRequest = PreviewCreativeRequest1 -"""Preview request using format_id to identify creative format.""" +PreviewCreativeSingleRequest = PreviewCreativeRequest1 +"""Single preview request with creative_manifest and optional format_id - request_type='single'.""" + +PreviewCreativeBatchRequest = PreviewCreativeRequest2 +"""Batch preview request with array of requests (1-50) - request_type='batch'.""" + +PreviewCreativeVariantRequest = PreviewCreativeRequest3 +"""Variant preview request using variant_id - request_type='variant'.""" -PreviewCreativeManifestRequest = PreviewCreativeRequest2 -"""Preview request using creative_manifest_url to identify creative.""" + +# Get Products Request Variants +GetProductsRefineRequest = GetProductsRequest3 +"""Get products request in refine mode - buying_mode='refine'. + +Used to iterate on previous product results with refinement actions +(include, omit, more_like_this) at request, product, or proposal scope. + +Example: + ```python + from adcp import GetProductsRefineRequest + + request = GetProductsRefineRequest( + buying_mode="refine", + account={"account_id": "acc_123"}, + refine=[{"action": "more_like_this", "product_id": "prod_456"}] + ) + ``` +""" + +# Performance Feedback Request Variants +ProvidePerformanceFeedbackByMediaBuyRequest = ProvidePerformanceFeedbackRequest1 +"""Performance feedback request identified by media_buy_id (required).""" + +ProvidePerformanceFeedbackByBuyerRefRequest = ProvidePerformanceFeedbackRequest2 +"""Performance feedback request identified by buyer_ref (required).""" # Update Media Buy Request Variants UpdateMediaBuyPackagesRequest = UpdateMediaBuyRequest1 @@ -333,25 +498,51 @@ def process_result(result: SyncCatalogResult) -> None: GetCreativeDeliveryByCreativeRequest = GetCreativeDeliveryRequest3 """Request creative delivery by creative_ids.""" +# Get Products Request Variants (by buying_mode) +GetProductsBriefRequest = GetProductsRequest1 +"""Get products in brief mode - buying_mode='brief', requires brief text.""" + +GetProductsWholesaleRequest = GetProductsRequest2 +"""Get products in wholesale mode - buying_mode='wholesale', raw inventory.""" + +# Get Signals Request Variants +GetSignalsDiscoveryRequest = GetSignalsRequest1 +"""Discover signals by natural language spec - signal_spec required.""" + +GetSignalsLookupRequest = GetSignalsRequest2 +"""Look up signals by IDs - signal_ids required.""" + +# SI Send Message Request Variants +SiSendTextMessageRequest = SiSendMessageRequest1 +"""Send a text message to the brand agent - message required.""" + +SiSendActionResponseRequest = SiSendMessageRequest2 +"""Send an action response to the brand agent - action_response required.""" + # ============================================================================ # ACTIVATION KEY ALIASES # ============================================================================ -# Note: Activation key schema changed from property_id/property_tag variants -# to segment_id/key_value variants. Import directly from _generated: -# from adcp.types._generated import ActivationKey1 as SegmentIdActivationKey -# from adcp.types._generated import ActivationKey2 as KeyValueActivationKey -# These will be added once the types are regenerated with proper schema. + +SegmentIdActivationKey = ActivationKey1 +"""Activation key using segment ID targeting - type='segment_id'.""" + +KeyValueActivationKey = ActivationKey2 +"""Activation key using key-value pair targeting - type='key_value'.""" # ============================================================================ # PREVIEW/RENDER TYPE ALIASES # ============================================================================ # Preview Creative Response Variants -PreviewCreativeStaticResponse = PreviewCreativeResponse1 -"""Preview response with static renders (image/HTML snapshots).""" +PreviewCreativeSingleResponse = PreviewCreativeResponse1 +"""Single preview response with previews array and expires_at - response_type='single'.""" + +PreviewCreativeBatchResponse = PreviewCreativeResponse2 +"""Batch preview response with results array - response_type='batch'.""" + +PreviewCreativeVariantResponse = PreviewCreativeResponse3 +"""Variant preview response with variant_id and rendered pieces - response_type='variant'.""" -PreviewCreativeInteractiveResponse = PreviewCreativeResponse2 -"""Preview response with interactive renders (iframe embedding).""" # Preview Render Aliases (discriminated union by output_format) UrlPreviewRender = PreviewRender1 @@ -745,6 +936,32 @@ def process_result(result: SyncCatalogResult) -> None: ``` """ +AuthorizedAgentsBySignalId = AuthorizedAgents4 +"""Authorized agent for specific signal IDs. + +This variant uses authorization_type='signal_ids' for agents authorized +to resell specific signals identified by their IDs. + +Fields: +- authorization_type: Literal['signal_ids'] +- authorized_for: Human-readable description of signals authorization +- signal_ids: List of SignalId (non-empty) +- url: Authorized signals agent's API endpoint URL +""" + +AuthorizedAgentsBySignalTag = AuthorizedAgents5 +"""Authorized agent for signals matching tags. + +This variant uses authorization_type='signal_tags' for agents authorized +to resell signals identified by matching tags. + +Fields: +- authorization_type: Literal['signal_tags'] +- authorized_for: Human-readable description of signals authorization +- signal_tags: List of SignalTag (non-empty) +- url: Authorized signals agent's API endpoint URL +""" + # ============================================================================ # UNION TYPE ALIASES - For Type Hints and Pattern Matching # ============================================================================ @@ -790,6 +1007,8 @@ def format_destination(dest: Destination) -> str: | AuthorizedAgentsByPropertyTag | AuthorizedAgentsByInlineProperties | AuthorizedAgentsByPublisherProperties + | AuthorizedAgentsBySignalId + | AuthorizedAgentsBySignalTag ) """Union type for all authorized agent variants. @@ -829,6 +1048,50 @@ def filter_products(props: PublisherProperties) -> None: ``` """ +# ============================================================================ +# SIGNAL PRICING OPTION ALIASES - Pricing Model Discriminated Unions +# ============================================================================ +# SignalPricingOption is a discriminated union with three pricing models. +# Variants 5/6/7 combine the model-specific fields with pricing_option_id. + +CpmSignalPricingOption = SignalPricingOption5 +"""Signal pricing option with model='cpm' - fixed cost per thousand impressions.""" + +PercentOfMediaSignalPricingOption = SignalPricingOption6 +"""Signal pricing option with model='percent_of_media' - percentage of media spend.""" + +FlatFeeSignalPricingOption = SignalPricingOption7 +"""Signal pricing option with model='flat_fee' - fixed charge per reporting period.""" + +# ============================================================================ +# SYNC AUDIENCES INPUT ALIASES +# ============================================================================ +# The Audience input type for SyncAudiencesRequest is exported here following +# the same pattern as SyncCreativeResult and SyncCatalogResult. + +SyncAudiencesAudience = SyncAudiencesAudienceInternal +"""Audience segment payload for SyncAudiencesRequest.audiences[]. + +Required: audience_id (buyer's identifier for the audience). +Optional: name, consent_basis, add (AudienceMember items), remove, delete. + +Example: + ```python + from adcp import SyncAudiencesAudience, SyncAudiencesRequest + + request = SyncAudiencesRequest( + account={"account_id": "acc_123"}, + audiences=[ + SyncAudiencesAudience( + audience_id="seg_456", + name="High-value customers", + consent_basis="consent" + ) + ] + ) + ``` +""" + # ============================================================================ # PRICING OPTION UNION TYPE - For Type Hints Without RootModel Wrapper # ============================================================================ @@ -843,7 +1106,9 @@ def filter_products(props: PublisherProperties) -> None: | CpcvPricingOption | CpvPricingOption | CppPricingOption + | CpaPricingOption | FlatRatePricingOption + | TimeBasedPricingOption ) """Union type for all pricing option variants. @@ -880,6 +1145,12 @@ def get_pricing(options: list[PricingOption]) -> None: # ============================================================================ __all__ = [ + # Account reference variants + "AccountReferenceById", + "AccountReferenceByNaturalKey", + # Activation key variants + "SegmentIdActivationKey", + "KeyValueActivationKey", # Activation responses "ActivateSignalSuccessResponse", "ActivateSignalErrorResponse", @@ -898,6 +1169,8 @@ def get_pricing(options: list[PricingOption]) -> None: "AuthorizedAgentsByPropertyTag", "AuthorizedAgentsByInlineProperties", "AuthorizedAgentsByPublisherProperties", + "AuthorizedAgentsBySignalId", + "AuthorizedAgentsBySignalTag", # Authorized agent union "AuthorizedAgent", # Build creative responses @@ -909,6 +1182,8 @@ def get_pricing(options: list[PricingOption]) -> None: # Content standards responses "CreateContentStandardsSuccessResponse", "CreateContentStandardsErrorResponse", + "UpdateContentStandardsSuccessResponse", + "UpdateContentStandardsErrorResponse", "GetContentStandardsSuccessResponse", "GetContentStandardsErrorResponse", "ListContentStandardsSuccessResponse", @@ -930,11 +1205,26 @@ def get_pricing(options: list[PricingOption]) -> None: "ProvidePerformanceFeedbackSuccessResponse", "ProvidePerformanceFeedbackErrorResponse", # Preview creative requests - "PreviewCreativeFormatRequest", - "PreviewCreativeManifestRequest", + "PreviewCreativeSingleRequest", + "PreviewCreativeBatchRequest", + "PreviewCreativeVariantRequest", # Preview creative responses - "PreviewCreativeStaticResponse", - "PreviewCreativeInteractiveResponse", + "PreviewCreativeSingleResponse", + "PreviewCreativeBatchResponse", + "PreviewCreativeVariantResponse", + # Get products request variants + "GetProductsBriefRequest", + "GetProductsWholesaleRequest", + "GetProductsRefineRequest", + # Get signals request variants + "GetSignalsDiscoveryRequest", + "GetSignalsLookupRequest", + # Performance feedback request variants + "ProvidePerformanceFeedbackByMediaBuyRequest", + "ProvidePerformanceFeedbackByBuyerRefRequest", + # SI send message request variants + "SiSendTextMessageRequest", + "SiSendActionResponseRequest", # Sync accounts responses "SyncAccountsSuccessResponse", "SyncAccountsErrorResponse", @@ -958,6 +1248,15 @@ def get_pricing(options: list[PricingOption]) -> None: # Validate content delivery responses "ValidateContentDeliverySuccessResponse", "ValidateContentDeliveryErrorResponse", + # Get account financials responses + "GetAccountFinancialsSuccessResponse", + "GetAccountFinancialsErrorResponse", + # Sync audiences responses + "SyncAudiencesSuccessResponse", + "SyncAudiencesErrorResponse", + # Get creative features responses + "GetCreativeFeaturesSuccessResponse", + "GetCreativeFeaturesErrorResponse", # Package type aliases "Package", # Publisher properties types @@ -981,4 +1280,15 @@ def get_pricing(options: list[PricingOption]) -> None: "Destination", # Pricing option union "PricingOption", + # Signal pricing option variants + "CpmSignalPricingOption", + "FlatFeeSignalPricingOption", + "PercentOfMediaSignalPricingOption", + # Sync audiences input type + "SyncAudiencesAudience", + "ConsentBasis", + # Status (backward compat - delivery status, not invoice status) + "MediaBuyDeliveryStatus", + # Catalog field binding semantic alias + "CatalogGroupBinding", ] diff --git a/src/adcp/types/generated_poc/account/get_account_financials_request.py b/src/adcp/types/generated_poc/account/get_account_financials_request.py new file mode 100644 index 00000000..b3b73c58 --- /dev/null +++ b/src/adcp/types/generated_poc/account/get_account_financials_request.py @@ -0,0 +1,33 @@ +# generated by datamodel-codegen: +# filename: account/get_account_financials_request.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field + +from ..core import account_ref +from ..core import context as context_1 +from ..core import date_range +from ..core import ext as ext_1 + + +class GetAccountFinancialsRequest(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + account: Annotated[ + account_ref.AccountReference, + Field(description='Account to query financials for. Must be an operator-billed account.'), + ] + context: context_1.ContextObject | None = None + ext: ext_1.ExtensionObject | None = None + period: Annotated[ + date_range.DateRange | None, + Field( + description='Date range for the spend summary. Defaults to the current billing cycle if omitted.' + ), + ] = None diff --git a/src/adcp/types/generated_poc/account/get_account_financials_response.py b/src/adcp/types/generated_poc/account/get_account_financials_response.py new file mode 100644 index 00000000..5dd08bef --- /dev/null +++ b/src/adcp/types/generated_poc/account/get_account_financials_response.py @@ -0,0 +1,210 @@ +# generated by datamodel-codegen: +# filename: account/get_account_financials_response.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from datetime import date as date_aliased +from enum import Enum +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field, RootModel + +from ..core import account_ref +from ..core import context as context_1 +from ..core import date_range, error +from ..core import ext as ext_1 + + +class LastTopUp(AdCPBaseModel): + amount: Annotated[float, Field(description='Top-up amount', ge=0.0)] + date: Annotated[date_aliased, Field(description='Date of top-up')] + + +class Balance(AdCPBaseModel): + available: Annotated[float, Field(description='Remaining prepaid balance', ge=0.0)] + last_top_up: Annotated[LastTopUp | None, Field(description='Most recent balance top-up')] = None + + +class Credit(AdCPBaseModel): + available_credit: Annotated[ + float, + Field(description='Remaining credit available (credit_limit minus outstanding balance)'), + ] + credit_limit: Annotated[float, Field(description='Maximum outstanding balance allowed', ge=0.0)] + utilization_percent: Annotated[ + float | None, + Field(description='Credit utilization as a percentage (0-100)', ge=0.0, le=100.0), + ] = None + + +class Status(Enum): + draft = 'draft' + issued = 'issued' + paid = 'paid' + past_due = 'past_due' + void = 'void' + + +class Invoice(AdCPBaseModel): + amount: Annotated[float, Field(description='Invoice total in currency', ge=0.0)] + due_date: Annotated[date_aliased | None, Field(description='Payment due date')] = None + invoice_id: Annotated[str, Field(description='Seller-assigned invoice identifier')] + paid_date: Annotated[ + date_aliased | None, + Field(description="Date payment was received. Present when status is 'paid'."), + ] = None + period: Annotated[ + date_range.DateRange | None, Field(description='Billing period covered by this invoice') + ] = None + status: Annotated[Status, Field(description='Invoice status')] + + +class PaymentStatus(Enum): + current = 'current' + past_due = 'past_due' + suspended = 'suspended' + + +class Spend(AdCPBaseModel): + media_buy_count: Annotated[ + int | None, Field(description='Number of active media buys in the period', ge=0) + ] = None + total_spend: Annotated[ + float, Field(description='Total spend in the period, in currency', ge=0.0) + ] + + +class GetAccountFinancialsResponse2(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + context: context_1.ContextObject | None = None + errors: Annotated[list[error.Error], Field(description='Operation-level errors', min_length=1)] + ext: ext_1.ExtensionObject | None = None + + +class GetAccountFinancialsResponse1(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + account: Annotated[ + account_ref.AccountReference, + Field(description='Account reference, echoed from the request'), + ] + balance: Annotated[ + Balance | None, Field(description='Prepay balance. Present for prepay accounts.') + ] = None + context: context_1.ContextObject | None = None + credit: Annotated[ + Credit | None, + Field( + description='Credit status. Present for credit-based accounts (payment_terms like net_30).' + ), + ] = None + currency: Annotated[ + str, + Field( + description='ISO 4217 currency code for all monetary amounts in this response', + pattern='^[A-Z]{3}$', + ), + ] + ext: ext_1.ExtensionObject | None = None + invoices: Annotated[ + list[Invoice] | None, + Field(description='Recent invoices. Sellers may limit the number returned.'), + ] = None + payment_status: Annotated[ + PaymentStatus | None, + Field( + description='Overall payment status. current: all obligations met. past_due: one or more invoices overdue. suspended: account suspended due to payment issues.' + ), + ] = None + payment_terms: Annotated[ + str | None, Field(description="Payment terms in effect (e.g., 'net_30', 'prepay')") + ] = None + period: Annotated[ + date_range.DateRange, + Field( + description='The actual period covered by spend data. May differ from the requested period if the seller adjusts to billing cycle boundaries.' + ), + ] + spend: Annotated[Spend | None, Field(description='Spend summary for the period')] = None + timezone: Annotated[ + str, + Field( + description="IANA timezone of the seller's billing day boundaries (e.g., 'America/New_York'). All dates in this response — period, invoice periods, due dates — are calendar dates in this timezone. Buyers in a different timezone should expect spend boundaries to differ from their own calendar day." + ), + ] + + +class GetAccountFinancialsResponse( + RootModel[GetAccountFinancialsResponse1 | GetAccountFinancialsResponse2] +): + root: Annotated[ + GetAccountFinancialsResponse1 | GetAccountFinancialsResponse2, + Field( + description='Financial status for an operator-billed account. Returns spend summary, credit/balance status, payment status, and invoice history. The level of detail varies by seller — only account, currency, and period are guaranteed on success.', + examples=[ + { + 'data': { + 'account': {'account_id': 'acc_acme_001'}, + 'credit': { + 'available_credit': 54770.0, + 'credit_limit': 100000.0, + 'utilization_percent': 45.23, + }, + 'currency': 'USD', + 'invoices': [ + { + 'amount': 38500.0, + 'due_date': '2026-02-28', + 'invoice_id': 'inv_2026_01', + 'paid_date': '2026-02-15', + 'period': {'end': '2026-01-31', 'start': '2026-01-01'}, + 'status': 'paid', + } + ], + 'payment_status': 'current', + 'payment_terms': 'net_30', + 'period': {'end': '2026-02-28', 'start': '2026-02-01'}, + 'spend': {'media_buy_count': 3, 'total_spend': 45230.0}, + 'timezone': 'America/New_York', + }, + 'description': 'Credit account — current, with invoice history', + }, + { + 'data': { + 'account': { + 'brand': {'domain': 'acme-corp.com'}, + 'operator': 'acme-corp.com', + }, + 'balance': { + 'available': 1800.0, + 'last_top_up': {'amount': 10000.0, 'date': '2026-02-01'}, + }, + 'currency': 'USD', + 'payment_status': 'current', + 'payment_terms': 'prepay', + 'period': {'end': '2026-02-28', 'start': '2026-02-01'}, + 'spend': {'media_buy_count': 2, 'total_spend': 8200.0}, + 'timezone': 'America/Los_Angeles', + }, + 'description': 'Prepay account with low balance', + }, + { + 'data': { + 'errors': [ + { + 'code': 'UNSUPPORTED_FEATURE', + 'message': "Financial data is not available for agent-billed accounts. The agent's own billing system is the source of truth.", + } + ] + }, + 'description': 'Agent-billed account — not supported', + }, + ], + title='Get Account Financials Response', + ), + ] diff --git a/src/adcp/types/generated_poc/account/list_accounts_request.py b/src/adcp/types/generated_poc/account/list_accounts_request.py index 3b7c23c3..56cf82c2 100644 --- a/src/adcp/types/generated_poc/account/list_accounts_request.py +++ b/src/adcp/types/generated_poc/account/list_accounts_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: account/list_accounts_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -18,6 +18,7 @@ class Status(Enum): active = 'active' pending_approval = 'pending_approval' + rejected = 'rejected' payment_required = 'payment_required' suspended = 'suspended' closed = 'closed' diff --git a/src/adcp/types/generated_poc/account/report_usage_request.py b/src/adcp/types/generated_poc/account/report_usage_request.py new file mode 100644 index 00000000..51074800 --- /dev/null +++ b/src/adcp/types/generated_poc/account/report_usage_request.py @@ -0,0 +1,91 @@ +# generated by datamodel-codegen: +# filename: account/report_usage_request.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field + +from ..core import account_ref +from ..core import context as context_1 +from ..core import datetime_range +from ..core import ext as ext_1 + + +class UsageItem(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + account: Annotated[ + account_ref.AccountReference, Field(description='Account for this usage record.') + ] + buyer_campaign_ref: Annotated[ + str | None, + Field( + description="The buyer's campaign reference (e.g., a media_buy_id). Used to group records by campaign." + ), + ] = None + currency: Annotated[str, Field(description='ISO 4217 currency code.', pattern='^[A-Z]{3}$')] + impressions: Annotated[ + int | None, Field(description='Impressions delivered using this vendor service.', ge=0) + ] = None + media_spend: Annotated[ + float | None, + Field( + description='Media spend in currency for the period. Required when a percent_of_media pricing model was used, so the vendor can verify the applied rate.', + ge=0.0, + ), + ] = None + pricing_option_id: Annotated[ + str | None, + Field( + description="Pricing option identifier from the vendor's discovery response (e.g., get_signals, list_content_standards). The vendor uses this to verify the correct rate was applied." + ), + ] = None + signal_agent_segment_id: Annotated[ + str | None, + Field(description='Signal identifier from get_signals. Required for signals agents.'), + ] = None + standards_id: Annotated[ + str | None, + Field( + description='Content standards configuration identifier. Required for governance agents.' + ), + ] = None + vendor_cost: Annotated[ + float, + Field( + description='Amount owed to the vendor for this record, denominated in currency.', + ge=0.0, + ), + ] + + +class ReportUsageRequest(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + context: context_1.ContextObject | None = None + ext: ext_1.ExtensionObject | None = None + idempotency_key: Annotated[ + str | None, + Field( + description='Client-generated unique key for this request. If a request with the same key has already been accepted, the server returns the original response without re-processing. Use a UUID or other unique identifier. Prevents duplicate billing on retries.' + ), + ] = None + reporting_period: Annotated[ + datetime_range.DatetimeRange, + Field( + description='The time range covered by this usage report. Applies to all records in the request.' + ), + ] + usage: Annotated[ + list[UsageItem], + Field( + description='One or more usage records. Each record is self-contained: it carries its own account and buyer_campaign_ref, allowing a single request to span multiple accounts and campaigns.', + min_length=1, + ), + ] diff --git a/src/adcp/types/generated_poc/account/report_usage_response.py b/src/adcp/types/generated_poc/account/report_usage_response.py new file mode 100644 index 00000000..4618f69b --- /dev/null +++ b/src/adcp/types/generated_poc/account/report_usage_response.py @@ -0,0 +1,35 @@ +# generated by datamodel-codegen: +# filename: account/report_usage_response.json +# timestamp: 2026-02-27T02:10:10+00:00 + +from __future__ import annotations + +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field + +from ..core import context as context_1 +from ..core import error +from ..core import ext as ext_1 + + +class ReportUsageResponse(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + accepted: Annotated[ + int, Field(description='Number of usage records successfully stored.', ge=0) + ] + context: context_1.ContextObject | None = None + errors: Annotated[ + list[error.Error] | None, + Field( + description="Validation errors for individual records. The field property identifies which record failed (e.g., 'usage[1].pricing_option_id')." + ), + ] = None + ext: ext_1.ExtensionObject | None = None + sandbox: Annotated[ + bool | None, + Field(description='When true, the account is a sandbox account and no billing occurred.'), + ] = None diff --git a/src/adcp/types/generated_poc/account/sync_accounts_request.py b/src/adcp/types/generated_poc/account/sync_accounts_request.py index f04e3f68..9fe94b2b 100644 --- a/src/adcp/types/generated_poc/account/sync_accounts_request.py +++ b/src/adcp/types/generated_poc/account/sync_accounts_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: account/sync_accounts_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -10,13 +10,13 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field +from ..core import brand_ref from ..core import context as context_1 from ..core import ext as ext_1 from ..core import push_notification_config as push_notification_config_1 class Billing(Enum): - brand = 'brand' operator = 'operator' agent = 'agent' @@ -26,32 +26,24 @@ class Account(AdCPBaseModel): extra='allow', ) billing: Annotated[ - Billing | None, + Billing, Field( - description="Who should be invoiced. brand: seller invoices the brand directly. operator: seller invoices the operator (agency). agent: agent consolidates billing across brands. Omit to accept the seller's default." + description='Who should be invoiced. operator: seller invoices the operator (agency or brand buying direct). agent: agent consolidates billing across brands. The seller must either accept this billing model or reject the request.' ), - ] = None - brand_id: Annotated[ - str | None, - Field( - description="Brand ID within the house portfolio (from brand.json). Required when the house has multiple brands (e.g., 'dove' under unilever.com, 'tide' under pg.com). Omit for single-brand houses.", - pattern='^[a-z0-9_]+$', - ), - ] = None - house: Annotated[ - str, + ] + brand: Annotated[ + brand_ref.BrandReference, Field( - description="House domain where brand.json is hosted (e.g., 'unilever.com', 'acme-corp.com'). This is the canonical identity anchor for the brand, resolved via /.well-known/brand.json. For single-brand houses, this alone identifies the brand.", - pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$', + description="Brand reference identifying the advertiser. Uses the brand's house domain and optional brand_id from brand.json." ), ] operator: Annotated[ - str | None, + str, Field( - description="Domain of the entity operating the seat (e.g., 'groupm.com', 'mindshare.com'). Verified against the brand's authorized_operators in brand.json. Omit if the brand operates its own seat.", + description="Domain of the entity operating on the brand's behalf (e.g., 'pinnacle-media.com'). When the brand operates directly, this is the brand's domain. Verified against the brand's authorized_operators in brand.json.", pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$', ), - ] = None + ] sandbox: Annotated[ bool | None, Field( @@ -84,6 +76,6 @@ class SyncAccountsRequest(AdCPBaseModel): push_notification_config: Annotated[ push_notification_config_1.PushNotificationConfig | None, Field( - description='Optional webhook for async notifications when account status changes (e.g., pending_approval transitions to active).' + description='Webhook for async notifications when account status changes (e.g., pending_approval transitions to active).' ), ] = None diff --git a/src/adcp/types/generated_poc/account/sync_accounts_response.py b/src/adcp/types/generated_poc/account/sync_accounts_response.py index 522264cb..c938b3ae 100644 --- a/src/adcp/types/generated_poc/account/sync_accounts_response.py +++ b/src/adcp/types/generated_poc/account/sync_accounts_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: account/sync_accounts_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -10,11 +10,19 @@ from adcp.types.base import AdCPBaseModel from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, RootModel +from ..core import brand_ref from ..core import context as context_1 from ..core import error from ..core import ext as ext_1 +class AccountScope(Enum): + operator = 'operator' + brand = 'brand' + operator_brand = 'operator_brand' + agent = 'agent' + + class Action(Enum): created = 'created' updated = 'updated' @@ -23,7 +31,6 @@ class Action(Enum): class Billing(Enum): - brand = 'brand' operator = 'operator' agent = 'agent' @@ -52,6 +59,7 @@ class Setup(AdCPBaseModel): class Status(Enum): active = 'active' pending_approval = 'pending_approval' + rejected = 'rejected' payment_required = 'payment_required' suspended = 'suspended' closed = 'closed' @@ -61,10 +69,10 @@ class Account(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_id: Annotated[ - str | None, + account_scope: Annotated[ + AccountScope | None, Field( - description="Seller-assigned account identifier. When billing is 'agent', multiple brands may share the same account_id." + description="How the seller scoped this account. operator: shared across all brands for this operator. brand: shared across all operators for this brand. operator_brand: dedicated to this operator+brand pair. agent: the agent's default account." ), ] = None action: Annotated[ @@ -75,41 +83,20 @@ class Account(AdCPBaseModel): ] billing: Annotated[ Billing | None, - Field( - description="Who is invoiced on this account. May differ from the requested billing if the seller doesn't support it." - ), - ] = None - brand_id: Annotated[ - str | None, - Field( - description='Brand ID within the house portfolio, echoed from request', - pattern='^[a-z0-9_]+$', - ), + Field(description='Who is invoiced on this account. Matches the requested billing model.'), ] = None + brand: Annotated[ + brand_ref.BrandReference, Field(description='Brand reference, echoed from the request') + ] credit_limit: CreditLimit | None = None errors: Annotated[ list[error.Error] | None, Field(description="Per-account errors (only present when action is 'failed')"), ] = None - house: Annotated[ - str, - Field( - description='House domain, echoed from the request', - pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$', - ), - ] name: Annotated[ str | None, Field(description='Human-readable account name assigned by the seller') ] = None - operator: Annotated[str | None, Field(description='Operator domain, echoed from request')] = ( - None - ) - parent_account_id: Annotated[ - str | None, - Field( - description='Parent account ID when this account is a sub-account under a shared billing account' - ), - ] = None + operator: Annotated[str, Field(description='Operator domain, echoed from request')] payment_terms: Annotated[ str | None, Field(description="Payment terms (e.g., 'net_30', 'prepay')") ] = None @@ -127,7 +114,7 @@ class Account(AdCPBaseModel): status: Annotated[ Status, Field( - description='Account status. active: ready for use. pending_approval: seller reviewing (credit, legal). payment_required: credit limit reached or funds depleted. suspended: was active, now paused. closed: terminated.' + description='Account status. active: ready for use. pending_approval: seller reviewing (credit, legal). rejected: seller declined the account request. payment_required: credit limit reached or funds depleted. suspended: was active, now paused. closed: was active, now terminated.' ), ] warnings: Annotated[ @@ -172,52 +159,66 @@ class SyncAccountsResponse(RootModel[SyncAccountsResponse1 | SyncAccountsRespons 'data': { 'accounts': [ { - 'account_id': 'sub_tide_001', + 'account_scope': 'operator_brand', 'action': 'created', - 'billing': 'agent', - 'brand_id': 'tide', - 'house': 'pg.com', - 'name': 'Tide (via GroupM)', - 'operator': 'groupm.com', - 'parent_account_id': 'acc_agent_house', + 'billing': 'operator', + 'brand': {'brand_id': 'spark', 'domain': 'nova-brands.com'}, + 'name': 'Spark (via Pinnacle)', + 'operator': 'pinnacle-media.com', 'status': 'active', }, { - 'account_id': 'acc_dove_pending', + 'account_scope': 'operator_brand', 'action': 'created', - 'billing': 'brand', - 'brand_id': 'dove', - 'house': 'unilever.com', - 'name': 'Dove', - 'operator': 'mindshare.com', + 'billing': 'operator', + 'brand': {'brand_id': 'glow', 'domain': 'nova-brands.com'}, + 'name': 'Glow', + 'operator': 'pinnacle-media.com', 'setup': { 'expires_at': '2026-03-10T00:00:00Z', - 'message': 'Credit application required for direct billing', - 'url': 'https://seller.com/onboard/dove', + 'message': 'Complete advertiser registration and credit application', + 'url': 'https://seller.example.com/advertiser-onboard', }, 'status': 'pending_approval', }, ] }, - 'description': 'Mixed results - one active, one pending approval', + 'description': 'Mixed results — one active, one pending approval', }, { 'data': { 'accounts': [ { - 'account_id': 'acc_agent_house', 'action': 'created', - 'billing': 'agent', - 'house': 'acme-corp.com', - 'name': 'Acme Corp (via agent)', - 'status': 'active', + 'brand': {'brand_id': 'clearance', 'domain': 'acme-corp.com'}, + 'operator': 'acme-corp.com', + 'status': 'rejected', 'warnings': [ - 'Direct billing (brand) not supported. Mapped to agent billing.' + 'Account request declined: advertiser category not accepted on this platform.' + ], + } + ] + }, + 'description': 'Rejected account — no account_id assigned', + }, + { + 'data': { + 'accounts': [ + { + 'action': 'failed', + 'brand': {'domain': 'acme-corp.com'}, + 'errors': [ + { + 'code': 'BILLING_NOT_SUPPORTED', + 'message': 'Operator billing is not supported. This seller only accepts agent billing.', + } ], + 'operator': 'acme-corp.com', + 'status': 'rejected', } ] }, - 'description': "Seller doesn't support direct billing, maps to agent billing", + 'description': 'Unsupported billing — seller rejects the request', }, ], title='Sync Accounts Response', diff --git a/src/adcp/types/generated_poc/content_standards/artifact.py b/src/adcp/types/generated_poc/content_standards/artifact.py index 4f5311d8..d398edaa 100644 --- a/src/adcp/types/generated_poc/content_standards/artifact.py +++ b/src/adcp/types/generated_poc/content_standards/artifact.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: content_standards/artifact.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -12,6 +12,7 @@ from ..core import format_id as format_id_1 from ..core import identifier +from ..core import provenance as provenance_1 class Role(Enum): @@ -24,26 +25,6 @@ class Role(Enum): description = 'description' -class Assets(AdCPBaseModel): - content: Annotated[str, Field(description='Text content')] - heading_level: Annotated[ - int | None, Field(description='Heading level (1-6), only for role=heading', ge=1, le=6) - ] = None - language: Annotated[ - str | None, - Field( - description="BCP 47 language tag for this text (e.g., 'en', 'es-MX'). Useful when artifact contains mixed-language content." - ), - ] = None - role: Annotated[ - Role | None, - Field( - description="Role of this text in the document. Use 'title' for the main artifact title, 'description' for summaries." - ), - ] = None - type: Literal['text'] - - class TranscriptSource(Enum): original_script = 'original_script' subtitles = 'subtitles' @@ -116,6 +97,30 @@ class AssetAccess(RootModel[AssetAccess1 | AssetAccess2 | AssetAccess3]): ] +class Assets(AdCPBaseModel): + content: Annotated[str, Field(description='Text content')] + heading_level: Annotated[ + int | None, Field(description='Heading level (1-6), only for role=heading', ge=1, le=6) + ] = None + language: Annotated[ + str | None, + Field( + description="BCP 47 language tag for this text (e.g., 'en', 'es-MX'). Useful when artifact contains mixed-language content." + ), + ] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field(description='Provenance for this text block, overrides artifact-level provenance'), + ] = None + role: Annotated[ + Role | None, + Field( + description="Role of this text in the document. Use 'title' for the main artifact title, 'description' for summaries." + ), + ] = None + type: Literal['text'] + + class Assets1(AdCPBaseModel): access: Annotated[AssetAccess | None, Field(description='Authentication for secured URLs')] = ( None @@ -123,6 +128,10 @@ class Assets1(AdCPBaseModel): alt_text: Annotated[str | None, Field(description='Alt text or image description')] = None caption: Annotated[str | None, Field(description='Image caption')] = None height: Annotated[int | None, Field(description='Image height in pixels')] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field(description='Provenance for this image, overrides artifact-level provenance'), + ] = None type: Literal['image'] url: Annotated[AnyUrl, Field(description='Image URL')] width: Annotated[int | None, Field(description='Image width in pixels')] = None @@ -133,6 +142,10 @@ class Assets2(AdCPBaseModel): None ) duration_ms: Annotated[int | None, Field(description='Video duration in milliseconds')] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field(description='Provenance for this video, overrides artifact-level provenance'), + ] = None thumbnail_url: Annotated[AnyUrl | None, Field(description='Video thumbnail URL')] = None transcript: Annotated[str | None, Field(description='Video transcript')] = None transcript_source: Annotated[ @@ -147,6 +160,10 @@ class Assets3(AdCPBaseModel): None ) duration_ms: Annotated[int | None, Field(description='Audio duration in milliseconds')] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field(description='Provenance for this audio, overrides artifact-level provenance'), + ] = None transcript: Annotated[str | None, Field(description='Audio transcript')] = None transcript_source: Annotated[ TranscriptSource1 | None, Field(description='How the transcript was generated') @@ -191,6 +208,12 @@ class Artifact(AdCPBaseModel): identifier.Identifier, Field(description='Identifier for the property where this artifact appears'), ] + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this artifact. Serves as the default provenance for all assets within this artifact — individual assets can override with their own provenance.' + ), + ] = None published_time: Annotated[ AwareDatetime | None, Field(description='When the artifact was published (ISO 8601 format)') ] = None diff --git a/src/adcp/types/generated_poc/content_standards/calibrate_content_response.py b/src/adcp/types/generated_poc/content_standards/calibrate_content_response.py index de6f660a..adbcd6ad 100644 --- a/src/adcp/types/generated_poc/content_standards/calibrate_content_response.py +++ b/src/adcp/types/generated_poc/content_standards/calibrate_content_response.py @@ -1,16 +1,18 @@ # generated by datamodel-codegen: # filename: content_standards/calibrate_content_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations from enum import Enum -from typing import Annotated, Any +from typing import Annotated from adcp.types.base import AdCPBaseModel from pydantic import Field, RootModel +from ..core import context as context_1 from ..core import error +from ..core import ext as ext_1 class Status(Enum): @@ -43,12 +45,11 @@ class CalibrateContentResponse1(AdCPBaseModel): confidence: Annotated[ float | None, Field(description='Model confidence in the verdict (0-1)', ge=0.0, le=1.0) ] = None - errors: Annotated[ - Any | None, Field(description='Field must not be present in success response') - ] = None + context: context_1.ContextObject | None = None explanation: Annotated[ str | None, Field(description='Detailed natural language explanation of the decision') ] = None + ext: ext_1.ExtensionObject | None = None features: Annotated[ list[Feature] | None, Field(description='Per-feature breakdown with explanations') ] = None @@ -58,10 +59,9 @@ class CalibrateContentResponse1(AdCPBaseModel): class CalibrateContentResponse2(AdCPBaseModel): + context: context_1.ContextObject | None = None errors: list[error.Error] - verdict: Annotated[ - Any | None, Field(description='Field must not be present in error response') - ] = None + ext: ext_1.ExtensionObject | None = None class CalibrateContentResponse(RootModel[CalibrateContentResponse1 | CalibrateContentResponse2]): diff --git a/src/adcp/types/generated_poc/content_standards/content_standards.py b/src/adcp/types/generated_poc/content_standards/content_standards.py index 11e41709..7da5da7b 100644 --- a/src/adcp/types/generated_poc/content_standards/content_standards.py +++ b/src/adcp/types/generated_poc/content_standards/content_standards.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: content_standards/content_standards.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -10,6 +10,7 @@ from pydantic import Field from ..core import ext as ext_1 +from ..core import pricing_option from ..enums import channels from . import artifact @@ -63,6 +64,13 @@ class ContentStandards(AdCPBaseModel): description='Natural language policy describing acceptable and unacceptable content contexts. Used by LLMs and human reviewers to make judgments.' ), ] = None + pricing_options: Annotated[ + list[pricing_option.PricingOption] | None, + Field( + description='Pricing options for this content standards service. The buyer passes the selected pricing_option_id in report_usage for billing verification.', + min_length=1, + ), + ] = None standards_id: Annotated[ str, Field(description='Unique identifier for this standards configuration') ] diff --git a/src/adcp/types/generated_poc/content_standards/create_content_standards_response.py b/src/adcp/types/generated_poc/content_standards/create_content_standards_response.py index 9aa7d29d..a1c12fd7 100644 --- a/src/adcp/types/generated_poc/content_standards/create_content_standards_response.py +++ b/src/adcp/types/generated_poc/content_standards/create_content_standards_response.py @@ -1,10 +1,10 @@ # generated by datamodel-codegen: # filename: content_standards/create_content_standards_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations -from typing import Annotated, Any +from typing import Annotated from adcp.types.base import AdCPBaseModel from pydantic import Field, RootModel @@ -16,9 +16,6 @@ class CreateContentStandardsResponse1(AdCPBaseModel): context: context_1.ContextObject | None = None - errors: Annotated[ - Any | None, Field(description='Field must not be present in success response') - ] = None ext: ext_1.ExtensionObject | None = None standards_id: Annotated[ str, Field(description='Unique identifier for the created standards configuration') @@ -35,9 +32,6 @@ class CreateContentStandardsResponse2(AdCPBaseModel): context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None - standards_id: Annotated[ - Any | None, Field(description='Field must not be present in error response') - ] = None class CreateContentStandardsResponse( diff --git a/src/adcp/types/generated_poc/content_standards/get_content_standards_response.py b/src/adcp/types/generated_poc/content_standards/get_content_standards_response.py index 60513c78..ec7f720c 100644 --- a/src/adcp/types/generated_poc/content_standards/get_content_standards_response.py +++ b/src/adcp/types/generated_poc/content_standards/get_content_standards_response.py @@ -1,10 +1,10 @@ # generated by datamodel-codegen: # filename: content_standards/get_content_standards_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations -from typing import Annotated, Any +from typing import Annotated from adcp.types.base import AdCPBaseModel from pydantic import Field, RootModel @@ -19,16 +19,10 @@ class GetContentStandardsResponse2(AdCPBaseModel): context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None - standards_id: Annotated[ - Any | None, Field(description='Field must not be present in error response') - ] = None class GetContentStandardsResponse1(ContentStandards): context: context_1.ContextObject | None = None - errors: Annotated[ - Any | None, Field(description='Field must not be present in success response') - ] = None class GetContentStandardsResponse( diff --git a/src/adcp/types/generated_poc/content_standards/get_media_buy_artifacts_request.py b/src/adcp/types/generated_poc/content_standards/get_media_buy_artifacts_request.py index be3446ba..9b936f76 100644 --- a/src/adcp/types/generated_poc/content_standards/get_media_buy_artifacts_request.py +++ b/src/adcp/types/generated_poc/content_standards/get_media_buy_artifacts_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: content_standards/get_media_buy_artifacts_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -10,6 +10,7 @@ from adcp.types.base import AdCPBaseModel from pydantic import AwareDatetime, ConfigDict, Field +from ..core import account_ref from ..core import context as context_1 from ..core import ext as ext_1 @@ -55,10 +56,10 @@ class TimeRange(AdCPBaseModel): class GetMediaBuyArtifactsRequest(AdCPBaseModel): - account_id: Annotated[ - str | None, + account: Annotated[ + account_ref.AccountReference | None, Field( - description='Filter artifacts to a specific account. When provided, only returns artifacts for media buys belonging to this account. When omitted, returns artifacts across all accessible accounts. Optional if the agent has a single account.' + description='Filter artifacts to a specific account. When omitted, returns artifacts across all accessible accounts.' ), ] = None context: context_1.ContextObject | None = None diff --git a/src/adcp/types/generated_poc/content_standards/get_media_buy_artifacts_response.py b/src/adcp/types/generated_poc/content_standards/get_media_buy_artifacts_response.py index 1ef0ee8d..6e9939a6 100644 --- a/src/adcp/types/generated_poc/content_standards/get_media_buy_artifacts_response.py +++ b/src/adcp/types/generated_poc/content_standards/get_media_buy_artifacts_response.py @@ -1,11 +1,11 @@ # generated by datamodel-codegen: # filename: content_standards/get_media_buy_artifacts_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations from enum import Enum -from typing import Annotated, Any +from typing import Annotated from adcp.types.base import AdCPBaseModel from pydantic import AwareDatetime, Field, RootModel @@ -52,9 +52,6 @@ class GetMediaBuyArtifactsResponse2(AdCPBaseModel): context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None - media_buy_id: Annotated[ - Any | None, Field(description='Field must not be present in error response') - ] = None class Artifact(AdCPBaseModel): @@ -88,9 +85,6 @@ class GetMediaBuyArtifactsResponse1(AdCPBaseModel): list[Artifact], Field(description='Delivery records with full artifact content') ] context: context_1.ContextObject | None = None - errors: Annotated[ - Any | None, Field(description='Field must not be present in success response') - ] = None ext: ext_1.ExtensionObject | None = None media_buy_id: Annotated[str, Field(description='Media buy these artifacts belong to')] pagination: pagination_response.PaginationResponse | None = None diff --git a/src/adcp/types/generated_poc/content_standards/list_content_standards_response.py b/src/adcp/types/generated_poc/content_standards/list_content_standards_response.py index 7620f442..fd902413 100644 --- a/src/adcp/types/generated_poc/content_standards/list_content_standards_response.py +++ b/src/adcp/types/generated_poc/content_standards/list_content_standards_response.py @@ -1,10 +1,10 @@ # generated by datamodel-codegen: # filename: content_standards/list_content_standards_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations -from typing import Annotated, Any +from typing import Annotated from adcp.types.base import AdCPBaseModel from pydantic import Field, RootModel @@ -20,16 +20,10 @@ class ListContentStandardsResponse2(AdCPBaseModel): context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None - standards: Annotated[ - Any | None, Field(description='Field must not be present in error response') - ] = None class ListContentStandardsResponse1(AdCPBaseModel): context: context_1.ContextObject | None = None - errors: Annotated[ - Any | None, Field(description='Field must not be present in success response') - ] = None ext: ext_1.ExtensionObject | None = None pagination: pagination_response.PaginationResponse | None = None standards: Annotated[ diff --git a/src/adcp/types/generated_poc/content_standards/update_content_standards_response.py b/src/adcp/types/generated_poc/content_standards/update_content_standards_response.py index a257598f..7e13af7a 100644 --- a/src/adcp/types/generated_poc/content_standards/update_content_standards_response.py +++ b/src/adcp/types/generated_poc/content_standards/update_content_standards_response.py @@ -1,20 +1,32 @@ # generated by datamodel-codegen: # filename: content_standards/update_content_standards_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations -from typing import Annotated +from typing import Annotated, Literal from adcp.types.base import AdCPBaseModel -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, RootModel from ..core import context as context_1 from ..core import error from ..core import ext as ext_1 -class UpdateContentStandardsResponse(AdCPBaseModel): +class UpdateContentStandardsResponse1(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + context: context_1.ContextObject | None = None + ext: ext_1.ExtensionObject | None = None + standards_id: Annotated[str, Field(description='ID of the updated standards configuration')] + success: Annotated[ + Literal[True], Field(description='Indicates the update was applied successfully') + ] + + +class UpdateContentStandardsResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) @@ -26,9 +38,19 @@ class UpdateContentStandardsResponse(AdCPBaseModel): ] = None context: context_1.ContextObject | None = None errors: Annotated[ - list[error.Error] | None, Field(description='Errors that occurred during the update') - ] = None + list[error.Error], Field(description='Errors that occurred during the update', min_length=1) + ] ext: ext_1.ExtensionObject | None = None - standards_id: Annotated[ - str | None, Field(description='ID of the updated standards configuration') - ] = None + success: Annotated[Literal[False], Field(description='Indicates the update failed')] + + +class UpdateContentStandardsResponse( + RootModel[UpdateContentStandardsResponse1 | UpdateContentStandardsResponse2] +): + root: Annotated[ + UpdateContentStandardsResponse1 | UpdateContentStandardsResponse2, + Field( + description='Response from updating a content standards configuration', + title='Update Content Standards Response', + ), + ] diff --git a/src/adcp/types/generated_poc/content_standards/validate_content_delivery_response.py b/src/adcp/types/generated_poc/content_standards/validate_content_delivery_response.py index d25c79fc..d311c819 100644 --- a/src/adcp/types/generated_poc/content_standards/validate_content_delivery_response.py +++ b/src/adcp/types/generated_poc/content_standards/validate_content_delivery_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: content_standards/validate_content_delivery_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -56,9 +56,6 @@ class Summary(AdCPBaseModel): class ValidateContentDeliveryResponse1(AdCPBaseModel): context: context_1.ContextObject | None = None - errors: Annotated[ - Any | None, Field(description='Field must not be present in success response') - ] = None ext: ext_1.ExtensionObject | None = None results: Annotated[list[Result], Field(description='Per-record evaluation results')] summary: Annotated[Summary, Field(description='Summary counts across all records')] @@ -68,9 +65,6 @@ class ValidateContentDeliveryResponse2(AdCPBaseModel): context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None - summary: Annotated[ - Any | None, Field(description='Field must not be present in error response') - ] = None class ValidateContentDeliveryResponse( diff --git a/src/adcp/types/generated_poc/core/account.py b/src/adcp/types/generated_poc/core/account.py index aeb045d1..09a4ba4c 100644 --- a/src/adcp/types/generated_poc/core/account.py +++ b/src/adcp/types/generated_poc/core/account.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/account.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -8,13 +8,20 @@ from typing import Annotated from adcp.types.base import AdCPBaseModel -from pydantic import ConfigDict, Field +from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field +from . import brand_ref from . import ext as ext_1 -class Billing(Enum): +class AccountScope(Enum): + operator = 'operator' brand = 'brand' + operator_brand = 'operator_brand' + agent = 'agent' + + +class Billing(Enum): operator = 'operator' agent = 'agent' @@ -24,9 +31,26 @@ class CreditLimit(AdCPBaseModel): currency: Annotated[str, Field(pattern='^[A-Z]{3}$')] +class Setup(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + expires_at: Annotated[ + AwareDatetime | None, Field(description='When this setup link expires.') + ] = None + message: Annotated[str, Field(description="Human-readable description of what's needed.")] + url: Annotated[ + AnyUrl | None, + Field( + description='URL where the human can complete the required action (credit application, legal agreement, add funds).' + ), + ] = None + + class Status(Enum): active = 'active' pending_approval = 'pending_approval' + rejected = 'rejected' payment_required = 'payment_required' suspended = 'suspended' closed = 'closed' @@ -37,13 +61,19 @@ class Account(AdCPBaseModel): extra='allow', ) account_id: Annotated[str, Field(description='Unique identifier for this account')] + account_scope: Annotated[ + AccountScope | None, + Field( + description="How the seller scoped this account. operator: shared across all brands for this operator. brand: shared across all operators for this brand. operator_brand: dedicated to a specific operator+brand combination. agent: the agent's default account with no brand or operator association." + ), + ] = None advertiser: Annotated[ str | None, Field(description='The advertiser whose rates apply to this account') ] = None billing: Annotated[ Billing | None, Field( - description='Who is invoiced on this account. brand: seller invoices the brand directly. operator: seller invoices the operator (agency). agent: agent consolidates billing.' + description='Who is invoiced on this account. operator: seller invoices the operator (agency or brand buying direct). agent: agent consolidates billing.' ), ] = None billing_proxy: Annotated[ @@ -52,31 +82,21 @@ class Account(AdCPBaseModel): description='Optional intermediary who receives invoices on behalf of the advertiser (e.g., agency)' ), ] = None - brand_id: Annotated[ - str | None, - Field( - description='Brand ID within the house portfolio (from brand.json)', - pattern='^[a-z0-9_]+$', - ), + brand: Annotated[ + brand_ref.BrandReference | None, + Field(description='Brand reference identifying the advertiser'), ] = None credit_limit: Annotated[ CreditLimit | None, Field(description='Maximum outstanding balance allowed') ] = None ext: ext_1.ExtensionObject | None = None - house: Annotated[ - str | None, - Field( - description='House domain where brand.json is hosted. Canonical identity anchor for the brand.', - pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$', - ), - ] = None name: Annotated[ str, Field(description="Human-readable account name (e.g., 'Acme', 'Acme c/o Pinnacle')") ] operator: Annotated[ str | None, Field( - description='Domain of the entity operating this account', + description="Domain of the entity operating this account. When the brand operates directly, this is the brand's domain.", pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$', ), ] = None @@ -92,9 +112,15 @@ class Account(AdCPBaseModel): description='When true, this is a sandbox account. All requests using this account_id are treated as sandbox — no real platform calls, no real spend.' ), ] = None + setup: Annotated[ + Setup | None, + Field( + description="Present when status is 'pending_approval'. Contains next steps for completing account activation." + ), + ] = None status: Annotated[ Status, Field( - description='Account status. pending_approval: seller reviewing (credit, contracts). payment_required: credit limit reached or funds depleted. suspended: was active, now paused. closed: terminated.' + description='Account status. pending_approval: seller reviewing (credit, contracts). rejected: seller declined the account request. payment_required: credit limit reached or funds depleted. suspended: was active, now paused. closed: was active, now terminated.' ), ] diff --git a/src/adcp/types/generated_poc/core/account_ref.py b/src/adcp/types/generated_poc/core/account_ref.py new file mode 100644 index 00000000..5710788e --- /dev/null +++ b/src/adcp/types/generated_poc/core/account_ref.py @@ -0,0 +1,58 @@ +# generated by datamodel-codegen: +# filename: core/account_ref.json +# timestamp: 2026-02-27T02:10:10+00:00 + +from __future__ import annotations + +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field, RootModel + +from . import brand_ref + + +class AccountReference1(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + account_id: Annotated[ + str, + Field( + description='Seller-assigned account identifier (from sync_accounts or list_accounts)' + ), + ] + + +class AccountReference2(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + brand: Annotated[ + brand_ref.BrandReference, Field(description='Brand reference identifying the advertiser') + ] + operator: Annotated[ + str, + Field( + description="Domain of the entity operating on the brand's behalf. When the brand operates directly, this is the brand's domain.", + pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$', + ), + ] + + +class AccountReference(RootModel[AccountReference1 | AccountReference2]): + root: Annotated[ + AccountReference1 | AccountReference2, + Field( + description='Reference to an account by seller-assigned ID or natural key. Use account_id when the buyer manages accounts (e.g., picked from list_accounts). Use the natural key (brand + operator) when the seller resolves accounts internally.', + examples=[ + {'account_id': 'acc_acme_001'}, + {'brand': {'domain': 'acme-corp.com'}, 'operator': 'acme-corp.com'}, + { + 'brand': {'brand_id': 'spark', 'domain': 'nova-brands.com'}, + 'operator': 'pinnacle-media.com', + }, + ], + title='Account Reference', + ), + ] diff --git a/src/adcp/types/generated_poc/core/assets/audio_asset.py b/src/adcp/types/generated_poc/core/assets/audio_asset.py index 82d8d02b..199e4f62 100644 --- a/src/adcp/types/generated_poc/core/assets/audio_asset.py +++ b/src/adcp/types/generated_poc/core/assets/audio_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/audio_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,6 +10,8 @@ from adcp.types.base import AdCPBaseModel from pydantic import AnyUrl, ConfigDict, Field +from .. import provenance as provenance_1 + class BitDepth(IntEnum): integer_16 = 16 @@ -48,6 +50,12 @@ class AudioAsset(AdCPBaseModel): ] = None file_size_bytes: Annotated[int | None, Field(description='File size in bytes', ge=1)] = None loudness_lufs: Annotated[float | None, Field(description='Integrated loudness in LUFS')] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None sampling_rate_hz: Annotated[ int | None, Field(description='Sampling rate in Hz (e.g., 44100, 48000, 96000)') ] = None diff --git a/src/adcp/types/generated_poc/core/assets/brief_asset.py b/src/adcp/types/generated_poc/core/assets/brief_asset.py new file mode 100644 index 00000000..dc2eb5d7 --- /dev/null +++ b/src/adcp/types/generated_poc/core/assets/brief_asset.py @@ -0,0 +1,11 @@ +# generated by datamodel-codegen: +# filename: core/assets/brief_asset.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from ..creative_brief import CreativeBrief + + +class BriefAsset(CreativeBrief): + pass diff --git a/src/adcp/types/generated_poc/core/assets/catalog_asset.py b/src/adcp/types/generated_poc/core/assets/catalog_asset.py new file mode 100644 index 00000000..4d6bdd80 --- /dev/null +++ b/src/adcp/types/generated_poc/core/assets/catalog_asset.py @@ -0,0 +1,11 @@ +# generated by datamodel-codegen: +# filename: core/assets/catalog_asset.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from ..catalog import Catalog + + +class CatalogAsset(Catalog): + pass diff --git a/src/adcp/types/generated_poc/core/assets/css_asset.py b/src/adcp/types/generated_poc/core/assets/css_asset.py index 4d85f393..1fd4e3b8 100644 --- a/src/adcp/types/generated_poc/core/assets/css_asset.py +++ b/src/adcp/types/generated_poc/core/assets/css_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/css_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,6 +9,8 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field +from .. import provenance as provenance_1 + class CssAsset(AdCPBaseModel): model_config = ConfigDict( @@ -18,3 +20,9 @@ class CssAsset(AdCPBaseModel): media: Annotated[ str | None, Field(description="CSS media query context (e.g., 'screen', 'print')") ] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None diff --git a/src/adcp/types/generated_poc/core/assets/daast_asset.py b/src/adcp/types/generated_poc/core/assets/daast_asset.py index 5bb5de12..3ad16bf8 100644 --- a/src/adcp/types/generated_poc/core/assets/daast_asset.py +++ b/src/adcp/types/generated_poc/core/assets/daast_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/daast_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -11,6 +11,7 @@ from ...enums import daast_tracking_event from ...enums import daast_version as daast_version_1 +from .. import provenance as provenance_1 class DaastAsset1(AdCPBaseModel): @@ -30,6 +31,12 @@ class DaastAsset1(AdCPBaseModel): duration_ms: Annotated[ int | None, Field(description='Expected audio duration in milliseconds (if known)', ge=0) ] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None tracking_events: Annotated[ list[daast_tracking_event.DaastTrackingEvent] | None, Field(description='Tracking events supported by this DAAST tag'), @@ -58,6 +65,12 @@ class DaastAsset2(AdCPBaseModel): duration_ms: Annotated[ int | None, Field(description='Expected audio duration in milliseconds (if known)', ge=0) ] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None tracking_events: Annotated[ list[daast_tracking_event.DaastTrackingEvent] | None, Field(description='Tracking events supported by this DAAST tag'), diff --git a/src/adcp/types/generated_poc/core/assets/html_asset.py b/src/adcp/types/generated_poc/core/assets/html_asset.py index 264a03a0..3368453e 100644 --- a/src/adcp/types/generated_poc/core/assets/html_asset.py +++ b/src/adcp/types/generated_poc/core/assets/html_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/html_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,6 +9,8 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field +from .. import provenance as provenance_1 + class Accessibility(AdCPBaseModel): alt_text: Annotated[ @@ -37,4 +39,10 @@ class HtmlAsset(AdCPBaseModel): Field(description='Self-declared accessibility properties for this opaque creative'), ] = None content: Annotated[str, Field(description='HTML content')] + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None version: Annotated[str | None, Field(description="HTML version (e.g., 'HTML5')")] = None diff --git a/src/adcp/types/generated_poc/core/assets/image_asset.py b/src/adcp/types/generated_poc/core/assets/image_asset.py index 9c48fc44..f18c6c58 100644 --- a/src/adcp/types/generated_poc/core/assets/image_asset.py +++ b/src/adcp/types/generated_poc/core/assets/image_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/image_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,6 +9,8 @@ from adcp.types.base import AdCPBaseModel from pydantic import AnyUrl, ConfigDict, Field +from .. import provenance as provenance_1 + class ImageAsset(AdCPBaseModel): model_config = ConfigDict( @@ -19,5 +21,11 @@ class ImageAsset(AdCPBaseModel): str | None, Field(description='Image file format (jpg, png, gif, webp, etc.)') ] = None height: Annotated[int, Field(description='Height in pixels', ge=1)] + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None url: Annotated[AnyUrl, Field(description='URL to the image asset')] width: Annotated[int, Field(description='Width in pixels', ge=1)] diff --git a/src/adcp/types/generated_poc/core/assets/javascript_asset.py b/src/adcp/types/generated_poc/core/assets/javascript_asset.py index 8fe88f11..b84ba8dd 100644 --- a/src/adcp/types/generated_poc/core/assets/javascript_asset.py +++ b/src/adcp/types/generated_poc/core/assets/javascript_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/javascript_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,6 +10,7 @@ from pydantic import ConfigDict, Field from ...enums import javascript_module_type +from .. import provenance as provenance_1 class Accessibility(AdCPBaseModel): @@ -43,3 +44,9 @@ class JavascriptAsset(AdCPBaseModel): javascript_module_type.JavascriptModuleType | None, Field(description='JavaScript module type'), ] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None diff --git a/src/adcp/types/generated_poc/core/assets/text_asset.py b/src/adcp/types/generated_poc/core/assets/text_asset.py index 17b7de6c..d69a1831 100644 --- a/src/adcp/types/generated_poc/core/assets/text_asset.py +++ b/src/adcp/types/generated_poc/core/assets/text_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/text_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,6 +9,8 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field +from .. import provenance as provenance_1 + class TextAsset(AdCPBaseModel): model_config = ConfigDict( @@ -18,3 +20,9 @@ class TextAsset(AdCPBaseModel): language: Annotated[str | None, Field(description="Language code (e.g., 'en', 'es', 'fr')")] = ( None ) + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None diff --git a/src/adcp/types/generated_poc/core/assets/url_asset.py b/src/adcp/types/generated_poc/core/assets/url_asset.py index 981c9cf5..34648de1 100644 --- a/src/adcp/types/generated_poc/core/assets/url_asset.py +++ b/src/adcp/types/generated_poc/core/assets/url_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/url_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,6 +10,7 @@ from pydantic import AnyUrl, ConfigDict, Field from ...enums import url_asset_type +from .. import provenance as provenance_1 class UrlAsset(AdCPBaseModel): @@ -19,6 +20,12 @@ class UrlAsset(AdCPBaseModel): description: Annotated[ str | None, Field(description='Description of what this URL points to') ] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None url: Annotated[AnyUrl, Field(description='URL reference')] url_type: Annotated[ url_asset_type.UrlAssetType | None, diff --git a/src/adcp/types/generated_poc/core/assets/vast_asset.py b/src/adcp/types/generated_poc/core/assets/vast_asset.py index d628daae..7c9c599b 100644 --- a/src/adcp/types/generated_poc/core/assets/vast_asset.py +++ b/src/adcp/types/generated_poc/core/assets/vast_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/vast_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -11,6 +11,7 @@ from ...enums import vast_tracking_event from ...enums import vast_version as vast_version_1 +from .. import provenance as provenance_1 class VastAsset1(AdCPBaseModel): @@ -31,6 +32,12 @@ class VastAsset1(AdCPBaseModel): duration_ms: Annotated[ int | None, Field(description='Expected video duration in milliseconds (if known)', ge=0) ] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None tracking_events: Annotated[ list[vast_tracking_event.VastTrackingEvent] | None, Field(description='Tracking events supported by this VAST tag'), @@ -64,6 +71,12 @@ class VastAsset2(AdCPBaseModel): duration_ms: Annotated[ int | None, Field(description='Expected video duration in milliseconds (if known)', ge=0) ] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None tracking_events: Annotated[ list[vast_tracking_event.VastTrackingEvent] | None, Field(description='Tracking events supported by this VAST tag'), diff --git a/src/adcp/types/generated_poc/core/assets/video_asset.py b/src/adcp/types/generated_poc/core/assets/video_asset.py index 86d466dc..9e38c366 100644 --- a/src/adcp/types/generated_poc/core/assets/video_asset.py +++ b/src/adcp/types/generated_poc/core/assets/video_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/video_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,6 +10,8 @@ from adcp.types.base import AdCPBaseModel from pydantic import AnyUrl, ConfigDict, Field +from .. import provenance as provenance_1 + class AudioBitDepth(IntEnum): integer_16 = 16 @@ -139,6 +141,12 @@ class VideoAsset(AdCPBaseModel): moov_atom_position: Annotated[ MoovAtomPosition | None, Field(description='Position of moov atom in MP4 container') ] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None scan_type: Annotated[ScanType | None, Field(description='Scan type of the video')] = None transcript_url: Annotated[ AnyUrl | None, Field(description='URL to text transcript of the video content') diff --git a/src/adcp/types/generated_poc/core/assets/webhook_asset.py b/src/adcp/types/generated_poc/core/assets/webhook_asset.py index e7953c26..2209e4d4 100644 --- a/src/adcp/types/generated_poc/core/assets/webhook_asset.py +++ b/src/adcp/types/generated_poc/core/assets/webhook_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/assets/webhook_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,6 +10,7 @@ from pydantic import AnyUrl, ConfigDict, Field from ...enums import http_method, universal_macro, webhook_response_type, webhook_security_method +from .. import provenance as provenance_1 class Security(AdCPBaseModel): @@ -31,6 +32,12 @@ class WebhookAsset(AdCPBaseModel): method: Annotated[http_method.HttpMethod | None, Field(description='HTTP method')] = ( http_method.HttpMethod.POST ) + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this asset, overrides manifest-level provenance' + ), + ] = None required_macros: Annotated[ list[universal_macro.UniversalMacro | str] | None, Field(description='Universal macros that must be provided for webhook to function'), diff --git a/src/adcp/types/generated_poc/core/attribution_window.py b/src/adcp/types/generated_poc/core/attribution_window.py index 16b2e3b5..302da22c 100644 --- a/src/adcp/types/generated_poc/core/attribution_window.py +++ b/src/adcp/types/generated_poc/core/attribution_window.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/attribution_window.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,29 +10,28 @@ from pydantic import ConfigDict, Field from ..enums import attribution_model +from . import duration class AttributionWindow(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - click_window_days: Annotated[ - int | None, - Field( - description='Click-through attribution window in days. Conversions occurring within this many days after a click are attributed to the ad.', - ge=0, - ), - ] = None model: Annotated[ attribution_model.AttributionModel, Field( description='Attribution model used to assign credit when multiple touchpoints exist' ), ] - view_window_days: Annotated[ - int | None, + post_click: Annotated[ + duration.Duration | None, + Field( + description='Post-click attribution window. Conversions occurring within this duration after a click are attributed to the ad.' + ), + ] = None + post_view: Annotated[ + duration.Duration | None, Field( - description='View-through attribution window in days. Conversions occurring within this many days after an ad impression (without click) are attributed to the ad.', - ge=0, + description='Post-view attribution window. Conversions occurring within this duration after an ad impression (without click) are attributed to the ad.' ), ] = None diff --git a/src/adcp/types/generated_poc/core/audience_member.py b/src/adcp/types/generated_poc/core/audience_member.py index 27f3f9b2..eb586656 100644 --- a/src/adcp/types/generated_poc/core/audience_member.py +++ b/src/adcp/types/generated_poc/core/audience_member.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/audience_member.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -26,6 +26,12 @@ class AudienceMember1(AdCPBaseModel): extra='allow', ) ext: ext_1.ExtensionObject | None = None + external_id: Annotated[ + str, + Field( + description="Buyer-assigned stable identifier for this audience member (e.g. CRM record ID, loyalty ID). Used for deduplication, removal, and cross-referencing with buyer systems. Adapters for CDPs that don't natively assign IDs can derive one (e.g. hash of the member's identifiers)." + ), + ] hashed_email: Annotated[ str, Field( @@ -54,6 +60,12 @@ class AudienceMember2(AdCPBaseModel): extra='allow', ) ext: ext_1.ExtensionObject | None = None + external_id: Annotated[ + str, + Field( + description="Buyer-assigned stable identifier for this audience member (e.g. CRM record ID, loyalty ID). Used for deduplication, removal, and cross-referencing with buyer systems. Adapters for CDPs that don't natively assign IDs can derive one (e.g. hash of the member's identifiers)." + ), + ] hashed_email: Annotated[ str | None, Field( @@ -82,6 +94,12 @@ class AudienceMember3(AdCPBaseModel): extra='allow', ) ext: ext_1.ExtensionObject | None = None + external_id: Annotated[ + str, + Field( + description="Buyer-assigned stable identifier for this audience member (e.g. CRM record ID, loyalty ID). Used for deduplication, removal, and cross-referencing with buyer systems. Adapters for CDPs that don't natively assign IDs can derive one (e.g. hash of the member's identifiers)." + ), + ] hashed_email: Annotated[ str | None, Field( @@ -109,7 +127,7 @@ class AudienceMember(RootModel[AudienceMember1 | AudienceMember2 | AudienceMembe root: Annotated[ AudienceMember1 | AudienceMember2 | AudienceMember3, Field( - description='Hashed identifiers for a CRM audience member. All identifiers must be normalized before hashing: emails to lowercase+trim, phone numbers to E.164 format (e.g. +12065551234). At least one identifier is required. Providing multiple identifiers for the same person improves match rates. Composite identifiers (e.g. hashed first name + last name + zip for Google Customer Match) are not yet standardized — use the ext field for platform-specific extensions.', + description='A CRM audience member identified by a buyer-assigned external_id and at least one matchable identifier. All identifiers must be normalized before hashing: emails to lowercase+trim, phone numbers to E.164 format (e.g. +12065551234). Providing multiple identifiers for the same person improves match rates. Composite identifiers (e.g. hashed first name + last name + zip for Google Customer Match) are not yet standardized — use the ext field for platform-specific extensions.', title='Audience Member', ), ] diff --git a/src/adcp/types/generated_poc/core/catalog.py b/src/adcp/types/generated_poc/core/catalog.py index 83071c08..904bdcd9 100644 --- a/src/adcp/types/generated_poc/core/catalog.py +++ b/src/adcp/types/generated_poc/core/catalog.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/catalog.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -14,6 +14,7 @@ from ..enums import event_type from ..enums import feed_format as feed_format_1 from ..enums import update_frequency as update_frequency_1 +from . import catalog_field_mapping class Gtin(RootModel[str]): @@ -49,6 +50,13 @@ class Catalog(AdCPBaseModel): min_length=1, ), ] = None + feed_field_mappings: Annotated[ + list[catalog_field_mapping.CatalogFieldMapping] | None, + Field( + description='Declarative normalization rules for external feeds. Maps non-standard feed field names, date formats, price encodings, and image URLs to the AdCP catalog item schema. Applied during sync_catalogs ingestion. Supports field renames, named transforms (date, divide, boolean, split), static literal injection, and assignment of image URLs to typed asset pools.', + min_length=1, + ), + ] = None feed_format: Annotated[ feed_format_1.FeedFormat | None, Field( diff --git a/src/adcp/types/generated_poc/core/catalog_field_mapping.py b/src/adcp/types/generated_poc/core/catalog_field_mapping.py new file mode 100644 index 00000000..181f97e5 --- /dev/null +++ b/src/adcp/types/generated_poc/core/catalog_field_mapping.py @@ -0,0 +1,88 @@ +# generated by datamodel-codegen: +# filename: core/catalog_field_mapping.json +# timestamp: 2026-02-27T02:10:10+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Annotated, Any + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field + +from . import ext as ext_1 + + +class Transform(Enum): + date = 'date' + divide = 'divide' + boolean = 'boolean' + split = 'split' + + +class CatalogFieldMapping(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + asset_group_id: Annotated[ + str | None, + Field( + description="Places the feed field value (a URL) into a typed asset pool on the catalog item's assets array. The value is wrapped as an image or video asset in a group with this ID. Use standard group IDs: 'images_landscape', 'images_vertical', 'images_square', 'logo', 'video'. Mutually exclusive with catalog_field." + ), + ] = None + by: Annotated[ + float | None, + Field( + description="For transform 'divide': the divisor to apply (e.g., 100 to convert integer cents to decimal dollars).", + gt=0.0, + ), + ] = None + catalog_field: Annotated[ + str | None, + Field( + description="Target field on the catalog item schema, using dot notation for nested fields (e.g., 'name', 'price.amount', 'location.city'). Mutually exclusive with asset_group_id." + ), + ] = None + default: Annotated[ + Any | None, + Field( + description='Fallback value to use when feed_field is absent, null, or empty. Applied after any transform would have been applied. Allows optional feed fields to have a guaranteed baseline value.' + ), + ] = None + ext: ext_1.ExtensionObject | None = None + feed_field: Annotated[ + str | None, + Field( + description='Field name in the external feed record. Omit when injecting a static literal value (use the value property instead).' + ), + ] = None + format: Annotated[ + str | None, + Field( + description="For transform 'date': the input date format string (e.g., 'YYYYMMDD', 'MM/DD/YYYY', 'DD-MM-YYYY'). Output is always ISO 8601 (e.g., '2025-03-01'). Uses Unicode date pattern tokens." + ), + ] = None + separator: Annotated[ + str | None, + Field( + description="For transform 'split': the separator character or string to split on. Defaults to ','." + ), + ] = ',' + timezone: Annotated[ + str | None, + Field( + description="For transform 'date': the timezone of the input value. IANA timezone identifier (e.g., 'UTC', 'America/New_York', 'Europe/Amsterdam'). Defaults to UTC when omitted." + ), + ] = None + transform: Annotated[ + Transform | None, + Field( + description='Named transform to apply to the feed field value before writing to the catalog schema. See transform-specific parameters (format, timezone, by, separator).' + ), + ] = None + value: Annotated[ + Any | None, + Field( + description='Static literal value to inject into catalog_field for every item, regardless of what the feed contains. Mutually exclusive with feed_field. Useful for fields the feed omits (e.g., currency when price is always USD, or a constant category value).' + ), + ] = None diff --git a/src/adcp/types/generated_poc/core/catchment.py b/src/adcp/types/generated_poc/core/catchment.py index b75afc71..92c9cb2f 100644 --- a/src/adcp/types/generated_poc/core/catchment.py +++ b/src/adcp/types/generated_poc/core/catchment.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/catchment.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -25,7 +25,7 @@ class Geometry(AdCPBaseModel): extra='forbid', ) coordinates: Annotated[ - Any, + list[Any], Field( description='GeoJSON coordinates array. For Polygon: array of linear rings. For MultiPolygon: array of polygons.' ), diff --git a/src/adcp/types/generated_poc/core/creative_asset.py b/src/adcp/types/generated_poc/core/creative_asset.py index 47d9e290..ebed423e 100644 --- a/src/adcp/types/generated_poc/core/creative_asset.py +++ b/src/adcp/types/generated_poc/core/creative_asset.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/creative_asset.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,19 +10,23 @@ from pydantic import ConfigDict, Field, StringConstraints from ..enums import creative_status -from . import catalog from . import format_id as format_id_1 +from . import provenance as provenance_1 from .assets import ( audio_asset, + brief_asset, + catalog_asset, css_asset, daast_asset, html_asset, image_asset, javascript_asset, + markdown_asset, text_asset, url_asset, vast_asset, video_asset, + webhook_asset, ) @@ -46,27 +50,24 @@ class CreativeAsset(AdCPBaseModel): ) assets: Annotated[ dict[ - Annotated[str, StringConstraints(pattern=r'^[a-zA-Z0-9_-]+$')], + Annotated[str, StringConstraints(pattern=r'^[a-z0-9_]+$')], image_asset.ImageAsset | video_asset.VideoAsset | audio_asset.AudioAsset + | vast_asset.VastAsset | text_asset.TextAsset + | url_asset.UrlAsset | html_asset.HtmlAsset - | css_asset.CssAsset | javascript_asset.JavascriptAsset - | vast_asset.VastAsset + | webhook_asset.WebhookAsset + | css_asset.CssAsset | daast_asset.DaastAsset - | url_asset.UrlAsset, + | markdown_asset.MarkdownAsset + | brief_asset.BriefAsset + | catalog_asset.CatalogAsset, ], - Field(description='Assets required by the format, keyed by asset_role'), + Field(description='Assets required by the format, keyed by asset_id'), ] - catalogs: Annotated[ - list[catalog.Catalog] | None, - Field( - description="Catalogs this creative renders. Each entry satisfies one of the format's catalog_requirements, matched by type. Each catalog can be inline (with items), a reference to a synced catalog (by catalog_id), or a URL to an external feed.", - min_length=1, - ), - ] = None creative_id: Annotated[str, Field(description='Unique identifier for the creative')] format_id: Annotated[ format_id_1.FormatId, @@ -88,6 +89,12 @@ class CreativeAsset(AdCPBaseModel): min_length=1, ), ] = None + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this creative. Serves as the default provenance for all manifests and assets within this creative. A manifest or asset with its own provenance replaces this object entirely (no field-level merging).' + ), + ] = None status: Annotated[ creative_status.CreativeStatus | None, Field( diff --git a/src/adcp/types/generated_poc/core/creative_assignment.py b/src/adcp/types/generated_poc/core/creative_assignment.py index ace0659b..90370694 100644 --- a/src/adcp/types/generated_poc/core/creative_assignment.py +++ b/src/adcp/types/generated_poc/core/creative_assignment.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/creative_assignment.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -23,5 +23,10 @@ class CreativeAssignment(AdCPBaseModel): ), ] = None weight: Annotated[ - float | None, Field(description='Delivery weight for this creative', ge=0.0, le=100.0) + float | None, + Field( + description='Relative delivery weight for this creative (0–100). When multiple creatives are assigned to the same package, weights determine impression distribution proportionally — a creative with weight 2 gets twice the delivery of weight 1. When omitted, the creative receives equal rotation with other unweighted creatives. A weight of 0 means the creative is assigned but paused (receives no delivery).', + ge=0.0, + le=100.0, + ), ] = None diff --git a/src/adcp/types/generated_poc/core/creative_brief.py b/src/adcp/types/generated_poc/core/creative_brief.py index 4cdab5d1..58301a38 100644 --- a/src/adcp/types/generated_poc/core/creative_brief.py +++ b/src/adcp/types/generated_poc/core/creative_brief.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/creative_brief.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -8,11 +8,16 @@ from typing import Annotated from adcp.types.base import AdCPBaseModel -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, RootModel +from ..enums import disclosure_position from . import reference_asset +class Jurisdiction(RootModel[str]): + root: Annotated[str, Field(pattern='^[A-Z]{2}(-[A-Z0-9]{1,3})?$')] + + class Messaging(AdCPBaseModel): model_config = ConfigDict( extra='allow', @@ -33,6 +38,65 @@ class Objective(Enum): engagement = 'engagement' +class RequiredDisclosure(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + jurisdictions: Annotated[ + list[Jurisdiction] | None, + Field( + description="Jurisdictions where this disclosure is required. ISO 3166-1 alpha-2 country codes or ISO 3166-2 subdivision codes (e.g., 'US', 'GB', 'US-NJ', 'CA-QC'). If omitted, the disclosure applies to all jurisdictions in the campaign.", + min_length=1, + ), + ] = None + language: Annotated[ + str | None, + Field( + description="Language of the disclosure text as a BCP 47 language tag (e.g., 'en', 'fr-CA', 'es'). When omitted, the disclosure is assumed to match the creative's language." + ), + ] = None + min_duration_ms: Annotated[ + int | None, + Field( + description='Minimum display duration in milliseconds. For video/audio disclosures, how long the disclosure must be visible or audible. For static formats, how long the disclosure must remain on screen before any auto-advance.', + ge=1, + ), + ] = None + position: Annotated[ + disclosure_position.DisclosurePosition | None, + Field( + description='Where the disclosure should appear within the creative. prominent: clearly visible in the main creative area; footer: at the bottom of visual creatives; audio: spoken in audio/video creatives; subtitle: displayed in the subtitle or closed-caption track; overlay: superimposed on video content; end_card: displayed on video end card; pre_roll: spoken or displayed before main content; companion: in companion ad unit alongside primary creative' + ), + ] = None + regulation: Annotated[ + str | None, + Field( + description="The regulation or legal authority requiring this disclosure (e.g., 'SEC Rule 156', 'FCA COBS 4.5', 'FDA 21 CFR 202')" + ), + ] = None + text: Annotated[str, Field(description='The disclosure text that must appear in the creative')] + + +class Compliance(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + prohibited_claims: Annotated[ + list[str] | None, + Field( + description='Claims that must not appear in creatives for this campaign. Creative agents should ensure generated content avoids these claims.', + min_length=1, + ), + ] = None + required_disclosures: Annotated[ + list[RequiredDisclosure] | None, + Field( + description='Disclosures that must appear in creatives for this campaign. Each disclosure specifies the text, where it should appear, and which jurisdictions require it.', + min_length=1, + ), + ] = None + + class CreativeBrief(AdCPBaseModel): model_config = ConfigDict( extra='allow', @@ -40,6 +104,12 @@ class CreativeBrief(AdCPBaseModel): audience: Annotated[ str | None, Field(description='Target audience description for this campaign') ] = None + compliance: Annotated[ + Compliance | None, + Field( + description='Regulatory and legal compliance requirements for this campaign. Campaign-specific, regional, and product-based — distinct from brand-level disclaimers in brand.json.' + ), + ] = None messaging: Annotated[ Messaging | None, Field(description='Messaging framework for the campaign') ] = None diff --git a/src/adcp/types/generated_poc/core/creative_brief_ref.py b/src/adcp/types/generated_poc/core/creative_brief_ref.py deleted file mode 100644 index 18a24356..00000000 --- a/src/adcp/types/generated_poc/core/creative_brief_ref.py +++ /dev/null @@ -1,39 +0,0 @@ -# generated by datamodel-codegen: -# filename: core/creative_brief_ref.json -# timestamp: 2026-02-23T01:56:40+00:00 - -from __future__ import annotations - -from typing import Annotated - -from pydantic import AnyUrl, Field, RootModel - -from . import creative_brief - - -class CreativeBriefReference(RootModel[creative_brief.CreativeBrief | AnyUrl]): - root: Annotated[ - creative_brief.CreativeBrief | AnyUrl, - Field( - description='Creative brief provided either as an inline object or a URL string pointing to a hosted brief', - examples=[ - { - 'data': { - 'audience': 'Gift shoppers aged 25-55', - 'messaging': { - 'cta': 'Shop Holiday Deals', - 'headline': 'Give the Gift of Style', - }, - 'name': 'Holiday Campaign 2025', - 'objective': 'conversion', - }, - 'description': 'Inline creative brief', - }, - { - 'data': 'https://cdn.example.com/briefs/holiday-2025.json', - 'description': 'URL string reference to hosted brief', - }, - ], - title='Creative Brief Reference', - ), - ] diff --git a/src/adcp/types/generated_poc/core/creative_filters.py b/src/adcp/types/generated_poc/core/creative_filters.py index c5c47db7..51fca988 100644 --- a/src/adcp/types/generated_poc/core/creative_filters.py +++ b/src/adcp/types/generated_poc/core/creative_filters.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/creative_filters.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -10,14 +10,15 @@ from pydantic import AwareDatetime, ConfigDict, Field from ..enums import creative_status +from . import account_ref class CreativeFilters(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_ids: Annotated[ - list[str] | None, + accounts: Annotated[ + list[account_ref.AccountReference] | None, Field( description='Filter creatives by owning accounts. Useful for agencies managing multiple client accounts.', min_length=1, diff --git a/src/adcp/types/generated_poc/core/creative_manifest.py b/src/adcp/types/generated_poc/core/creative_manifest.py index 0def580f..0917e878 100644 --- a/src/adcp/types/generated_poc/core/creative_manifest.py +++ b/src/adcp/types/generated_poc/core/creative_manifest.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/creative_manifest.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,16 +9,19 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field, StringConstraints -from . import catalog from . import ext as ext_1 from . import format_id as format_id_1 +from . import provenance as provenance_1 from .assets import ( audio_asset, + brief_asset, + catalog_asset, css_asset, daast_asset, html_asset, image_asset, javascript_asset, + markdown_asset, text_asset, url_asset, vast_asset, @@ -44,19 +47,15 @@ class CreativeManifest(AdCPBaseModel): | javascript_asset.JavascriptAsset | webhook_asset.WebhookAsset | css_asset.CssAsset - | daast_asset.DaastAsset, + | daast_asset.DaastAsset + | markdown_asset.MarkdownAsset + | brief_asset.BriefAsset + | catalog_asset.CatalogAsset, ], Field( description="Map of asset IDs to actual asset content. Each key MUST match an asset_id from the format's assets array (e.g., 'banner_image', 'clickthrough_url', 'video_file', 'vast_tag'). The asset_id is the technical identifier used to match assets to format requirements.\n\nIMPORTANT: Full validation requires format context. The format defines what type each asset_id should be. Standalone schema validation only checks structural conformance — each asset must match at least one valid asset type schema." ), ] - catalogs: Annotated[ - list[catalog.Catalog] | None, - Field( - description="Catalogs this creative renders. Each entry satisfies one of the format's catalog_requirements, matched by type. Tells the creative what data to display — product listings for a carousel, job vacancies for a recruitment ad, store locations for a locator. This is a data reference, not a campaign expansion directive; campaign structure and budget allocation are handled by create_media_buy packages. Each catalog can be inline (with items), a reference to a synced catalog (by catalog_id), or a URL to an external feed.", - min_length=1, - ), - ] = None ext: ext_1.ExtensionObject | None = None format_id: Annotated[ format_id_1.FormatId, @@ -64,3 +63,9 @@ class CreativeManifest(AdCPBaseModel): description="Format identifier this manifest is for. Can be a template format (id only) or a deterministic format (id + dimensions/duration). For dimension-specific creatives, include width/height/unit in the format_id to create a unique identifier (e.g., {id: 'display_static', width: 300, height: 250, unit: 'px'})." ), ] + provenance: Annotated[ + provenance_1.Provenance | None, + Field( + description='Provenance metadata for this creative manifest. Serves as the default provenance for all assets in this manifest. An asset with its own provenance replaces this object entirely (no field-level merging).' + ), + ] = None diff --git a/src/adcp/types/generated_poc/core/creative_policy.py b/src/adcp/types/generated_poc/core/creative_policy.py index 81c120fd..94976ed7 100644 --- a/src/adcp/types/generated_poc/core/creative_policy.py +++ b/src/adcp/types/generated_poc/core/creative_policy.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/creative_policy.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -23,6 +23,12 @@ class CreativePolicy(AdCPBaseModel): landing_page_requirement.LandingPageRequirement, Field(description='Landing page requirements'), ] + provenance_required: Annotated[ + bool | None, + Field( + description='Whether creatives must include provenance metadata. When true, the seller requires buyers to attach provenance declarations to creative submissions. The seller may independently verify claims via get_creative_features.' + ), + ] = None templates_available: Annotated[ bool, Field(description='Whether creative templates are provided') ] diff --git a/src/adcp/types/generated_poc/core/date_range.py b/src/adcp/types/generated_poc/core/date_range.py new file mode 100644 index 00000000..47738901 --- /dev/null +++ b/src/adcp/types/generated_poc/core/date_range.py @@ -0,0 +1,19 @@ +# generated by datamodel-codegen: +# filename: core/date_range.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from datetime import date as date_aliased +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field + + +class DateRange(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + end: Annotated[date_aliased, Field(description='End date (inclusive), ISO 8601')] + start: Annotated[date_aliased, Field(description='Start date (inclusive), ISO 8601')] diff --git a/src/adcp/types/generated_poc/core/datetime_range.py b/src/adcp/types/generated_poc/core/datetime_range.py new file mode 100644 index 00000000..b47e091f --- /dev/null +++ b/src/adcp/types/generated_poc/core/datetime_range.py @@ -0,0 +1,18 @@ +# generated by datamodel-codegen: +# filename: core/datetime_range.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import AwareDatetime, ConfigDict, Field + + +class DatetimeRange(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + end: Annotated[AwareDatetime, Field(description='End timestamp (inclusive), ISO 8601')] + start: Annotated[AwareDatetime, Field(description='Start timestamp (inclusive), ISO 8601')] diff --git a/src/adcp/types/generated_poc/core/delivery_metrics.py b/src/adcp/types/generated_poc/core/delivery_metrics.py index 4d0c1b11..03057bbc 100644 --- a/src/adcp/types/generated_poc/core/delivery_metrics.py +++ b/src/adcp/types/generated_poc/core/delivery_metrics.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/delivery_metrics.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -159,7 +159,11 @@ class DeliveryMetrics(AdCPBaseModel): ] = None clicks: Annotated[float | None, Field(description='Total clicks', ge=0.0)] = None completed_views: Annotated[ - float | None, Field(description='100% completions (for CPCV)', ge=0.0) + float | None, + Field( + description='Video/audio completions. When the package has a completed_views optimization goal with view_duration_seconds, completions are counted at that threshold rather than 100% completion.', + ge=0.0, + ), ] = None completion_rate: Annotated[ float | None, @@ -195,11 +199,25 @@ class DeliveryMetrics(AdCPBaseModel): engagement_rate: Annotated[ float | None, Field( - description='Platform-specific engagement rate (0.0 to 1.0). Definition varies by platform (e.g., likes+comments+shares/impressions on social, interactions/impressions on rich media).', + description='Platform-specific engagement rate (0.0 to 1.0). Typically engagements/impressions, but definition varies by platform.', ge=0.0, le=1.0, ), ] = None + engagements: Annotated[ + float | None, + Field( + description="Total engagements — direct interactions with the ad beyond viewing. Includes social reactions/comments/shares, story/unit opens, interactive overlay taps on CTV, companion banner interactions on audio. Platform-specific; corresponds to the 'engagements' optimization metric.", + ge=0.0, + ), + ] = None + follows: Annotated[ + float | None, + Field( + description='New followers, page likes, artist/podcast/channel subscribes attributed to this delivery.', + ge=0.0, + ), + ] = None frequency: Annotated[ float | None, Field( @@ -226,6 +244,13 @@ class DeliveryMetrics(AdCPBaseModel): le=1.0, ), ] = None + profile_visits: Annotated[ + float | None, + Field( + description="Visits to the brand's in-platform page (profile, artist page, channel, or storefront) attributed to this delivery. Does not include external website clicks.", + ge=0.0, + ), + ] = None quartile_data: Annotated[ QuartileData | None, Field(description='Video quartile completion data') ] = None @@ -239,6 +264,12 @@ class DeliveryMetrics(AdCPBaseModel): roas: Annotated[ float | None, Field(description='Return on ad spend (conversion_value / spend)', ge=0.0) ] = None + saves: Annotated[ + float | None, + Field( + description='Saves, bookmarks, playlist adds, pins attributed to this delivery.', ge=0.0 + ), + ] = None spend: Annotated[float | None, Field(description='Amount spent', ge=0.0)] = None viewability: Annotated[ Viewability | None, diff --git a/src/adcp/types/generated_poc/core/destination_item.py b/src/adcp/types/generated_poc/core/destination_item.py index df9d93f4..bbf35a92 100644 --- a/src/adcp/types/generated_poc/core/destination_item.py +++ b/src/adcp/types/generated_poc/core/destination_item.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/destination_item.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -11,6 +11,7 @@ from pydantic import AnyUrl, ConfigDict, Field from . import ext as ext_1 +from . import offering_asset_group from . import price as price_1 @@ -40,6 +41,13 @@ class DestinationItem(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) + assets: Annotated[ + list[offering_asset_group.OfferingAssetGroup] | None, + Field( + description="Typed creative asset pools for this destination. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (destination hero), 'images_vertical' (9:16 for Snap, Stories), 'images_square' (1:1). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + min_length=1, + ), + ] = None city: Annotated[str | None, Field(description='City name, if applicable.')] = None country: Annotated[ str | None, Field(description='ISO 3166-1 alpha-2 country code.', pattern='^[A-Z]{2}$') diff --git a/src/adcp/types/generated_poc/core/duration.py b/src/adcp/types/generated_poc/core/duration.py new file mode 100644 index 00000000..d5ba4140 --- /dev/null +++ b/src/adcp/types/generated_poc/core/duration.py @@ -0,0 +1,30 @@ +# generated by datamodel-codegen: +# filename: core/duration.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field + + +class Unit(Enum): + minutes = 'minutes' + hours = 'hours' + days = 'days' + campaign = 'campaign' + + +class Duration(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + interval: Annotated[ + int, Field(description="Number of time units. Must be 1 when unit is 'campaign'.", ge=1) + ] + unit: Annotated[ + Unit, Field(description="Time unit. 'campaign' spans the full campaign flight.") + ] diff --git a/src/adcp/types/generated_poc/core/education_item.py b/src/adcp/types/generated_poc/core/education_item.py index 53fd13b8..4b5575b2 100644 --- a/src/adcp/types/generated_poc/core/education_item.py +++ b/src/adcp/types/generated_poc/core/education_item.py @@ -1,10 +1,10 @@ # generated by datamodel-codegen: # filename: core/education_item.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations -from datetime import date +from datetime import date as date_aliased from enum import Enum from typing import Annotated @@ -12,6 +12,7 @@ from pydantic import AnyUrl, ConfigDict, Field from . import ext as ext_1 +from . import offering_asset_group from . import price as price_1 @@ -41,6 +42,13 @@ class EducationItem(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) + assets: Annotated[ + list[offering_asset_group.OfferingAssetGroup] | None, + Field( + description="Typed creative asset pools for this program. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (campus/program hero), 'images_vertical' (9:16 for Stories), 'logo' (institution logo). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + min_length=1, + ), + ] = None degree_type: Annotated[DegreeType | None, Field(description='Type of credential awarded.')] = ( None ) @@ -79,7 +87,7 @@ class EducationItem(AdCPBaseModel): program_id: Annotated[str, Field(description='Unique identifier for this program or course.')] school: Annotated[str, Field(description='Institution or provider name.')] start_date: Annotated[ - date | None, Field(description='Next available start date (ISO 8601 date).') + date_aliased | None, Field(description='Next available start date (ISO 8601 date).') ] = None subject: Annotated[ str | None, diff --git a/src/adcp/types/generated_poc/core/error.py b/src/adcp/types/generated_poc/core/error.py index 023ee8e9..943bde99 100644 --- a/src/adcp/types/generated_poc/core/error.py +++ b/src/adcp/types/generated_poc/core/error.py @@ -1,20 +1,32 @@ # generated by datamodel-codegen: # filename: core/error.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations +from enum import Enum from typing import Annotated, Any from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field +class Recovery(Enum): + transient = 'transient' + correctable = 'correctable' + terminal = 'terminal' + + class Error(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - code: Annotated[str, Field(description='Error code for programmatic handling')] + code: Annotated[ + str, + Field( + description='Error code for programmatic handling. Standard codes are defined in error-code.json and enable autonomous agent recovery. Sellers MAY use codes not in the standard vocabulary for platform-specific errors; agents MUST handle unknown codes gracefully by falling back to the recovery classification.' + ), + ] details: Annotated[ dict[str, Any] | None, Field(description='Additional task-specific error details') ] = None @@ -23,6 +35,12 @@ class Error(AdCPBaseModel): Field(description="Field path associated with the error (e.g., 'packages[0].targeting')"), ] = None message: Annotated[str, Field(description='Human-readable error message')] + recovery: Annotated[ + Recovery | None, + Field( + description='Agent recovery classification. transient: retry after delay (rate limit, service unavailable, timeout). correctable: fix the request and resend (invalid field, budget too low, creative rejected). terminal: requires human action (account suspended, payment required, account not found).' + ), + ] = None retry_after: Annotated[ float | None, Field(description='Seconds to wait before retrying the operation', ge=0.0) ] = None diff --git a/src/adcp/types/generated_poc/core/flight_item.py b/src/adcp/types/generated_poc/core/flight_item.py index 7a24f1fa..9244f668 100644 --- a/src/adcp/types/generated_poc/core/flight_item.py +++ b/src/adcp/types/generated_poc/core/flight_item.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/flight_item.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -10,6 +10,7 @@ from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field from . import ext as ext_1 +from . import offering_asset_group from . import price as price_1 @@ -42,6 +43,13 @@ class FlightItem(AdCPBaseModel): arrival_time: Annotated[ AwareDatetime | None, Field(description='Arrival date and time (ISO 8601).') ] = None + assets: Annotated[ + list[offering_asset_group.OfferingAssetGroup] | None, + Field( + description="Typed creative asset pools for this flight. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (destination hero), 'images_vertical' (9:16 for Stories), 'images_square' (1:1). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + min_length=1, + ), + ] = None departure_time: Annotated[ AwareDatetime | None, Field(description='Departure date and time (ISO 8601).') ] = None diff --git a/src/adcp/types/generated_poc/core/forecast_point.py b/src/adcp/types/generated_poc/core/forecast_point.py index d51c8a1b..fcd02c6d 100644 --- a/src/adcp/types/generated_poc/core/forecast_point.py +++ b/src/adcp/types/generated_poc/core/forecast_point.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/forecast_point.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -12,6 +12,22 @@ from . import forecast_range +class Metrics(AdCPBaseModel): + audience_size: forecast_range.ForecastRange | None = None + clicks: forecast_range.ForecastRange | None = None + completed_views: forecast_range.ForecastRange | None = None + engagements: forecast_range.ForecastRange | None = None + follows: forecast_range.ForecastRange | None = None + frequency: forecast_range.ForecastRange | None = None + grps: forecast_range.ForecastRange | None = None + impressions: forecast_range.ForecastRange | None = None + profile_visits: forecast_range.ForecastRange | None = None + reach: forecast_range.ForecastRange | None = None + saves: forecast_range.ForecastRange | None = None + spend: forecast_range.ForecastRange | None = None + views: forecast_range.ForecastRange | None = None + + class ForecastPoint(AdCPBaseModel): model_config = ConfigDict( extra='allow', @@ -24,8 +40,8 @@ class ForecastPoint(AdCPBaseModel): ), ] metrics: Annotated[ - dict[str, forecast_range.ForecastRange], + Metrics, Field( - description='Forecasted metric values at this budget level. Keys are either forecastable-metric values for delivery/engagement (impressions, reach, spend, etc.) or event-type values for outcomes (purchase, lead, app_install, etc.). Values are ForecastRange objects (low/mid/high). Use { "mid": value } for point estimates. Include spend when the platform predicts it will differ from budget.' + description='Forecasted metric values at this budget level. Keys are forecastable-metric enum values for delivery/engagement or event-type enum values for outcomes. Values are ForecastRange objects (low/mid/high). Use { "mid": value } for point estimates. Include spend when the platform predicts it will differ from budget. Additional keys beyond the documented properties are allowed for event-type values (purchase, lead, app_install, etc.).' ), ] diff --git a/src/adcp/types/generated_poc/core/format.py b/src/adcp/types/generated_poc/core/format.py index f25c5a04..28bdf570 100644 --- a/src/adcp/types/generated_poc/core/format.py +++ b/src/adcp/types/generated_poc/core/format.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/format.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,12 +10,19 @@ from adcp.types.base import AdCPBaseModel from pydantic import AnyUrl, ConfigDict, Field -from ..enums import available_metric, format_category, format_id_parameter, universal_macro +from ..enums import ( + available_metric, + disclosure_position, + format_category, + format_id_parameter, + universal_macro, +) from ..enums import wcag_level as wcag_level_1 from . import format_id as format_id_1 -from .requirements import audio_asset_requirements -from .requirements import catalog_requirements as catalog_requirements_1 +from . import overlay from .requirements import ( + audio_asset_requirements, + catalog_requirements, css_asset_requirements, daast_asset_requirements, html_asset_requirements, @@ -107,12 +114,67 @@ class Renders1(AdCPBaseModel): ] +class Accessibility(AdCPBaseModel): + requires_accessible_assets: Annotated[ + bool | None, + Field( + description='When true, all assets with x-accessibility fields must include those fields. For inspectable assets (image, video, audio), this means providing accessibility metadata like alt_text or captions. For opaque assets (HTML, JavaScript), this means providing self-declared accessibility properties.' + ), + ] = None + wcag_level: Annotated[ + wcag_level_1.WcagLevel, + Field( + description='WCAG conformance level that this format achieves. For format-rendered creatives, the format guarantees this level. For opaque creatives, the format requires assets that self-certify to this level.' + ), + ] + + +class FormatCard(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + format_id: Annotated[ + format_id_1.FormatId, + Field( + description='Creative format defining the card layout (typically format_card_standard)' + ), + ] + manifest: Annotated[ + dict[str, Any], + Field(description='Asset manifest for rendering the card, structure defined by the format'), + ] + + +class FormatCardDetailed(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + format_id: Annotated[ + format_id_1.FormatId, + Field( + description='Creative format defining the detailed card layout (typically format_card_detailed)' + ), + ] + manifest: Annotated[ + dict[str, Any], + Field( + description='Asset manifest for rendering the detailed card, structure defined by the format' + ), + ] + + class BaseGroupAsset(AdCPBaseModel): asset_id: Annotated[str, Field(description='Identifier for this asset within the group')] asset_role: Annotated[ str | None, Field( - description="Optional descriptive label for this asset's purpose. Not used for referencing assets in manifests—use asset_id instead. This field is for human-readable documentation and UI display only." + description="Descriptive label for this asset's purpose. For documentation and UI display only — manifests key assets by asset_id, not asset_role." + ), + ] = None + overlays: Annotated[ + list[overlay.Overlay] | None, + Field( + description="Publisher-controlled elements rendered on top of buyer content at this asset's position (e.g., carousel navigation arrows, slide indicators). Creative agents should avoid placing critical content within overlay bounds." ), ] = None required: Annotated[ @@ -131,32 +193,23 @@ class BaseIndividualAsset(AdCPBaseModel): asset_role: Annotated[ str | None, Field( - description="Optional descriptive label for this asset's purpose (e.g., 'hero_image', 'logo', 'third_party_tracking'). Not used for referencing assets in manifests—use asset_id instead. This field is for human-readable documentation and UI display only." + description="Descriptive label for this asset's purpose (e.g., 'hero_image', 'logo', 'third_party_tracking'). For documentation and UI display only — manifests key assets by asset_id, not asset_role." ), ] = None item_type: Annotated[ Literal['individual'], Field(description='Discriminator indicating this is an individual asset'), ] - required: Annotated[ - bool, + overlays: Annotated[ + list[overlay.Overlay] | None, Field( - description='Whether this asset is required (true) or optional (false). Required assets must be provided for a valid creative. Optional assets enhance the creative but are not mandatory.' - ), - ] - - -class Accessibility(AdCPBaseModel): - requires_accessible_assets: Annotated[ - bool | None, - Field( - description='When true, all assets with x-accessibility fields must include those fields. For inspectable assets (image, video, audio), this means providing accessibility metadata like alt_text or captions. For opaque assets (HTML, JavaScript), this means providing self-declared accessibility properties.' + description="Publisher-controlled elements rendered on top of buyer content at this asset's position (e.g., video player controls, publisher logos). Creative agents should avoid placing critical content (CTAs, logos, key copy) within overlay bounds." ), ] = None - wcag_level: Annotated[ - wcag_level_1.WcagLevel, + required: Annotated[ + bool, Field( - description='WCAG conformance level that this format achieves. For format-rendered creatives, the format guarantees this level. For opaque creatives, the format requires assets that self-certify to this level.' + description='Whether this asset is required (true) or optional (false). Required assets must be provided for a valid creative. Optional assets enhance the creative but are not mandatory.' ), ] @@ -233,75 +286,84 @@ class Assets15(BaseIndividualAsset): requirements: webhook_asset_requirements.WebhookAssetRequirements | None = None -class Assets17(BaseGroupAsset): +class Assets16(BaseIndividualAsset): + asset_type: Literal['brief'] = 'brief' + item_type: Literal['individual'] = 'individual' + + +class Assets17(BaseIndividualAsset): + asset_type: Literal['catalog'] = 'catalog' + item_type: Literal['individual'] = 'individual' + requirements: catalog_requirements.CatalogRequirements | None = None + + +class Assets19(BaseGroupAsset): asset_type: Literal['image'] = 'image' requirements: image_asset_requirements.ImageAssetRequirements | None = None -class Assets18(BaseGroupAsset): +class Assets20(BaseGroupAsset): asset_type: Literal['video'] = 'video' requirements: video_asset_requirements.VideoAssetRequirements | None = None -class Assets19(BaseGroupAsset): +class Assets21(BaseGroupAsset): asset_type: Literal['audio'] = 'audio' requirements: audio_asset_requirements.AudioAssetRequirements | None = None -class Assets20(BaseGroupAsset): +class Assets22(BaseGroupAsset): asset_type: Literal['text'] = 'text' requirements: text_asset_requirements.TextAssetRequirements | None = None -class Assets21(BaseGroupAsset): +class Assets23(BaseGroupAsset): asset_type: Literal['markdown'] = 'markdown' requirements: markdown_asset_requirements.MarkdownAssetRequirements | None = None -class Assets22(BaseGroupAsset): +class Assets24(BaseGroupAsset): asset_type: Literal['html'] = 'html' requirements: html_asset_requirements.HtmlAssetRequirements | None = None -class Assets23(BaseGroupAsset): +class Assets25(BaseGroupAsset): asset_type: Literal['css'] = 'css' requirements: css_asset_requirements.CssAssetRequirements | None = None -class Assets24(BaseGroupAsset): +class Assets26(BaseGroupAsset): asset_type: Literal['javascript'] = 'javascript' requirements: javascript_asset_requirements.JavascriptAssetRequirements | None = None -class Assets25(BaseGroupAsset): +class Assets27(BaseGroupAsset): asset_type: Literal['vast'] = 'vast' requirements: vast_asset_requirements.VastAssetRequirements | None = None -class Assets26(BaseGroupAsset): +class Assets28(BaseGroupAsset): asset_type: Literal['daast'] = 'daast' requirements: daast_asset_requirements.DaastAssetRequirements | None = None -class Assets27(BaseGroupAsset): +class Assets29(BaseGroupAsset): asset_type: Literal['url'] = 'url' requirements: url_asset_requirements.UrlAssetRequirements | None = None -class Assets28(BaseGroupAsset): +class Assets30(BaseGroupAsset): asset_type: Literal['webhook'] = 'webhook' requirements: webhook_asset_requirements.WebhookAssetRequirements | None = None -class Assets16(AdCPBaseModel): +class Assets18(AdCPBaseModel): asset_group_id: Annotated[ str, Field(description="Identifier for this asset group (e.g., 'product', 'slide', 'card')") ] assets: Annotated[ list[ - Assets17 - | Assets18 - | Assets19 + Assets19 | Assets20 | Assets21 | Assets22 @@ -311,6 +373,8 @@ class Assets16(AdCPBaseModel): | Assets26 | Assets27 | Assets28 + | Assets29 + | Assets30 ], Field(description='Assets within each repetition of this group'), ] @@ -340,40 +404,6 @@ class Assets16(AdCPBaseModel): ] = SelectionMode.sequential -class FormatCard(AdCPBaseModel): - model_config = ConfigDict( - extra='allow', - ) - format_id: Annotated[ - format_id_1.FormatId, - Field( - description='Creative format defining the card layout (typically format_card_standard)' - ), - ] - manifest: Annotated[ - dict[str, Any], - Field(description='Asset manifest for rendering the card, structure defined by the format'), - ] - - -class FormatCardDetailed(AdCPBaseModel): - model_config = ConfigDict( - extra='allow', - ) - format_id: Annotated[ - format_id_1.FormatId, - Field( - description='Creative format defining the detailed card layout (typically format_card_detailed)' - ), - ] - manifest: Annotated[ - dict[str, Any], - Field( - description='Asset manifest for rendering the detailed card, structure defined by the format' - ), - ] - - class Format(AdCPBaseModel): model_config = ConfigDict( extra='allow', @@ -405,19 +435,14 @@ class Format(AdCPBaseModel): | Assets14 | Assets15 | Assets16 + | Assets17 + | Assets18 ] | None, Field( description="Array of all assets supported for this format. Each asset is identified by its asset_id, which must be used as the key in creative manifests. Use the 'required' boolean on each asset to indicate whether it's mandatory." ), ] = None - catalog_requirements: Annotated[ - list[catalog_requirements_1.CatalogRequirements] | None, - Field( - description='Catalog feeds this format requires for rendering. Formats that display product listings, store locators, inventory availability, or promotional pricing declare what catalog types must be synced to the account. Buyers ensure the required catalogs are synced via sync_catalogs before submitting creatives in this format.', - min_length=1, - ), - ] = None delivery: Annotated[ dict[str, Any] | None, Field(description='Delivery method specifications (e.g., hosted, VAST, third-party tags)'), @@ -477,6 +502,13 @@ class Format(AdCPBaseModel): min_length=1, ), ] = None + supported_disclosure_positions: Annotated[ + list[disclosure_position.DisclosurePosition] | None, + Field( + description='Disclosure positions this format can render. Buyers use this to determine whether a format can satisfy their compliance requirements before submitting a creative. When omitted, the format makes no disclosure rendering guarantees — creative agents SHOULD treat this as incompatible with briefs that require specific disclosure positions. Values correspond to positions on creative-brief.json required_disclosures.', + min_length=1, + ), + ] = None supported_macros: Annotated[ list[universal_macro.UniversalMacro | str] | None, Field( diff --git a/src/adcp/types/generated_poc/core/frequency_cap.py b/src/adcp/types/generated_poc/core/frequency_cap.py index 1e141000..b21c7aac 100644 --- a/src/adcp/types/generated_poc/core/frequency_cap.py +++ b/src/adcp/types/generated_poc/core/frequency_cap.py @@ -1,19 +1,137 @@ # generated by datamodel-codegen: # filename: core/frequency_cap.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations from typing import Annotated from adcp.types.base import AdCPBaseModel -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, RootModel +from ..enums import reach_unit +from . import duration -class FrequencyCap(AdCPBaseModel): + +class FrequencyCap1(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + max_impressions: Annotated[ + int | None, + Field( + description="Maximum number of impressions per entity per window. For duration windows, implementations typically use a rolling window; 'campaign' applies a fixed cap across the full flight.", + ge=1, + ), + ] = None + per: Annotated[ + reach_unit.ReachUnit | None, + Field( + description='Entity granularity for impression counting. Required when max_impressions is set.' + ), + ] = None + suppress: Annotated[ + duration.Duration, + Field( + description='Cooldown period between consecutive exposures to the same entity. Prevents back-to-back ad delivery (e.g. {"interval": 60, "unit": "minutes"} for a 1-hour cooldown). Preferred over suppress_minutes.' + ), + ] + suppress_minutes: Annotated[ + float | None, + Field( + description='Deprecated — use suppress instead. Cooldown period in minutes between consecutive exposures to the same entity (e.g. 60 for a 1-hour cooldown).', + ge=0.0, + ), + ] = None + window: Annotated[ + duration.Duration | None, + Field( + description='Time window for the max_impressions cap (e.g. {"interval": 7, "unit": "days"} or {"interval": 1, "unit": "campaign"} for the full flight). Required when max_impressions is set.' + ), + ] = None + + +class FrequencyCap2(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + max_impressions: Annotated[ + int | None, + Field( + description="Maximum number of impressions per entity per window. For duration windows, implementations typically use a rolling window; 'campaign' applies a fixed cap across the full flight.", + ge=1, + ), + ] = None + per: Annotated[ + reach_unit.ReachUnit | None, + Field( + description='Entity granularity for impression counting. Required when max_impressions is set.' + ), + ] = None + suppress: Annotated[ + duration.Duration | None, + Field( + description='Cooldown period between consecutive exposures to the same entity. Prevents back-to-back ad delivery (e.g. {"interval": 60, "unit": "minutes"} for a 1-hour cooldown). Preferred over suppress_minutes.' + ), + ] = None + suppress_minutes: Annotated[ + float, + Field( + description='Deprecated — use suppress instead. Cooldown period in minutes between consecutive exposures to the same entity (e.g. 60 for a 1-hour cooldown).', + ge=0.0, + ), + ] + window: Annotated[ + duration.Duration | None, + Field( + description='Time window for the max_impressions cap (e.g. {"interval": 7, "unit": "days"} or {"interval": 1, "unit": "campaign"} for the full flight). Required when max_impressions is set.' + ), + ] = None + + +class FrequencyCap3(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) + max_impressions: Annotated[ + int, + Field( + description="Maximum number of impressions per entity per window. For duration windows, implementations typically use a rolling window; 'campaign' applies a fixed cap across the full flight.", + ge=1, + ), + ] + per: Annotated[ + reach_unit.ReachUnit | None, + Field( + description='Entity granularity for impression counting. Required when max_impressions is set.' + ), + ] = None + suppress: Annotated[ + duration.Duration | None, + Field( + description='Cooldown period between consecutive exposures to the same entity. Prevents back-to-back ad delivery (e.g. {"interval": 60, "unit": "minutes"} for a 1-hour cooldown). Preferred over suppress_minutes.' + ), + ] = None suppress_minutes: Annotated[ - float, Field(description='Minutes to suppress after impression', ge=0.0) + float | None, + Field( + description='Deprecated — use suppress instead. Cooldown period in minutes between consecutive exposures to the same entity (e.g. 60 for a 1-hour cooldown).', + ge=0.0, + ), + ] = None + window: Annotated[ + duration.Duration | None, + Field( + description='Time window for the max_impressions cap (e.g. {"interval": 7, "unit": "days"} or {"interval": 1, "unit": "campaign"} for the full flight). Required when max_impressions is set.' + ), + ] = None + + +class FrequencyCap(RootModel[FrequencyCap1 | FrequencyCap2 | FrequencyCap3]): + root: Annotated[ + FrequencyCap1 | FrequencyCap2 | FrequencyCap3, + Field( + description='Frequency capping settings for package-level application. Two types of frequency control can be used independently or together: suppress enforces a cooldown between consecutive exposures; max_impressions + per + window caps total exposures per entity in a time window. When both suppress and max_impressions are set, an impression is delivered only if both constraints permit it (AND semantics). At least one of suppress, suppress_minutes, or max_impressions must be set.', + title='Frequency Cap', + ), ] diff --git a/src/adcp/types/generated_poc/core/geo_breakdown_support.py b/src/adcp/types/generated_poc/core/geo_breakdown_support.py new file mode 100644 index 00000000..8dfd682b --- /dev/null +++ b/src/adcp/types/generated_poc/core/geo_breakdown_support.py @@ -0,0 +1,36 @@ +# generated by datamodel-codegen: +# filename: core/geo_breakdown_support.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field + +from ..enums import metro_system, postal_system + + +class GeographicBreakdownSupport(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + country: Annotated[ + bool | None, Field(description='Supports country-level geo breakdown (ISO 3166-1 alpha-2)') + ] = None + metro: Annotated[ + dict[metro_system.MetroAreaSystem, bool] | None, + Field( + description='Metro area breakdown support. Keys are metro-system enum values; true means supported.' + ), + ] = None + postal_area: Annotated[ + dict[postal_system.PostalCodeSystem, bool] | None, + Field( + description='Postal area breakdown support. Keys are postal-system enum values; true means supported.' + ), + ] = None + region: Annotated[ + bool | None, Field(description='Supports region/state-level geo breakdown (ISO 3166-2)') + ] = None diff --git a/src/adcp/types/generated_poc/core/hotel_item.py b/src/adcp/types/generated_poc/core/hotel_item.py index 67d59a2e..e2591377 100644 --- a/src/adcp/types/generated_poc/core/hotel_item.py +++ b/src/adcp/types/generated_poc/core/hotel_item.py @@ -1,15 +1,17 @@ # generated by datamodel-codegen: # filename: core/hotel_item.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations +from datetime import date as date_aliased from typing import Annotated from adcp.types.base import AdCPBaseModel from pydantic import AnyUrl, ConfigDict, Field from . import ext as ext_1 +from . import offering_asset_group from . import price as price_1 @@ -52,6 +54,13 @@ class HotelItem(AdCPBaseModel): min_length=1, ), ] = None + assets: Annotated[ + list[offering_asset_group.OfferingAssetGroup] | None, + Field( + description="Typed creative asset pools for this hotel. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (16:9 hero images), 'images_vertical' (9:16 for Snap, Stories), 'images_square' (1:1), 'logo'. Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + min_length=1, + ), + ] = None check_in_time: Annotated[ str | None, Field( @@ -98,3 +107,15 @@ class HotelItem(AdCPBaseModel): ), ] = None url: Annotated[AnyUrl | None, Field(description='Property landing page or booking URL.')] = None + valid_from: Annotated[ + date_aliased | None, + Field( + description="Date from which this item is available or this rate applies (ISO 8601, e.g., '2025-03-01'). Used for seasonal availability windows in feed imports." + ), + ] = None + valid_to: Annotated[ + date_aliased | None, + Field( + description="Date until which this item is available or this rate applies (ISO 8601, e.g., '2025-09-30'). Used for seasonal availability windows in feed imports." + ), + ] = None diff --git a/src/adcp/types/generated_poc/core/job_item.py b/src/adcp/types/generated_poc/core/job_item.py index aac6f355..d2140277 100644 --- a/src/adcp/types/generated_poc/core/job_item.py +++ b/src/adcp/types/generated_poc/core/job_item.py @@ -1,10 +1,10 @@ # generated by datamodel-codegen: # filename: core/job_item.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations -from datetime import date +from datetime import date as date_aliased from enum import Enum from typing import Annotated @@ -12,6 +12,7 @@ from pydantic import AnyUrl, ConfigDict, Field from . import ext as ext_1 +from . import offering_asset_group class EmploymentType(Enum): @@ -52,9 +53,16 @@ class JobItem(AdCPBaseModel): extra='allow', ) apply_url: Annotated[AnyUrl | None, Field(description='Direct application URL.')] = None + assets: Annotated[ + list[offering_asset_group.OfferingAssetGroup] | None, + Field( + description="Typed creative asset pools for this job. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (company/role hero), 'images_vertical' (9:16 for Stories), 'logo' (company logo). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + min_length=1, + ), + ] = None company_name: Annotated[str, Field(description='Hiring company or organization name.')] date_posted: Annotated[ - date | None, Field(description='Date the job was posted (ISO 8601 date).') + date_aliased | None, Field(description='Date the job was posted (ISO 8601 date).') ] = None description: Annotated[ str, @@ -103,5 +111,5 @@ class JobItem(AdCPBaseModel): str, Field(description="Job title (e.g., 'Senior Software Engineer', 'Marketing Manager').") ] valid_through: Annotated[ - date | None, Field(description='Application deadline (ISO 8601 date).') + date_aliased | None, Field(description='Application deadline (ISO 8601 date).') ] = None diff --git a/src/adcp/types/generated_poc/core/mcp_webhook_payload.py b/src/adcp/types/generated_poc/core/mcp_webhook_payload.py index bbb310ad..6dac980e 100644 --- a/src/adcp/types/generated_poc/core/mcp_webhook_payload.py +++ b/src/adcp/types/generated_poc/core/mcp_webhook_payload.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/mcp_webhook_payload.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -39,7 +39,7 @@ class McpWebhookPayload(AdCPBaseModel): operation_id: Annotated[ str | None, Field( - description='Publisher-defined operation identifier correlating a sequence of task updates across webhooks.' + description='Client-generated identifier that was embedded in the webhook URL by the buyer. Publishers echo this back in webhook payloads so clients can correlate notifications without parsing URL paths. Typically generated as a unique ID per task invocation.' ), ] = None result: Annotated[ diff --git a/src/adcp/types/generated_poc/core/media_buy.py b/src/adcp/types/generated_poc/core/media_buy.py index a20ddb87..1762c137 100644 --- a/src/adcp/types/generated_poc/core/media_buy.py +++ b/src/adcp/types/generated_poc/core/media_buy.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/media_buy.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -42,6 +42,12 @@ class MediaBuy(AdCPBaseModel): packages: Annotated[ list[package.Package], Field(description='Array of packages within this media buy') ] + rejection_reason: Annotated[ + str | None, + Field( + description="Reason provided by the seller when status is 'rejected'. Present only when status is 'rejected'." + ), + ] = None status: media_buy_status.MediaBuyStatus total_budget: Annotated[float, Field(description='Total budget amount', ge=0.0)] updated_at: Annotated[AwareDatetime | None, Field(description='Last update timestamp')] = None diff --git a/src/adcp/types/generated_poc/core/optimization_goal.py b/src/adcp/types/generated_poc/core/optimization_goal.py index e7851af8..63b1ef6e 100644 --- a/src/adcp/types/generated_poc/core/optimization_goal.py +++ b/src/adcp/types/generated_poc/core/optimization_goal.py @@ -1,60 +1,274 @@ # generated by datamodel-codegen: # filename: core/optimization_goal.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations -from typing import Annotated +from enum import Enum +from typing import Annotated, Literal from adcp.types.base import AdCPBaseModel -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, RootModel from ..enums import event_type as event_type_1 +from ..enums import reach_unit as reach_unit_1 +from . import duration + + +class Metric(Enum): + clicks = 'clicks' + views = 'views' + completed_views = 'completed_views' + viewed_seconds = 'viewed_seconds' + attention_seconds = 'attention_seconds' + attention_score = 'attention_score' + engagements = 'engagements' + follows = 'follows' + saves = 'saves' + profile_visits = 'profile_visits' + reach = 'reach' + + +class Target(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + kind: Literal['cost_per'] + value: Annotated[ + float, Field(description='Target cost per metric unit in the buy currency', gt=0.0) + ] + + +class Target1(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + kind: Literal['threshold_rate'] + value: Annotated[ + float, + Field( + description='Minimum per-impression value. Units depend on the metric: proportion (clicks, views, completed_views), seconds (viewed_seconds, attention_seconds), or score (attention_score).', + gt=0.0, + ), + ] + + +class TargetFrequency(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + max: Annotated[ + int | None, + Field( + description='Frequency at which an entity is considered saturated within the window. Impressions toward entities at or above this threshold are treated as lower-value. When both min and max are present, max must be greater than or equal to min. When omitted, the seller determines the saturation point.', + ge=1, + ), + ] = None + min: Annotated[ + int, + Field( + description='Minimum frequency for an entity to be considered meaningfully reached within the window. Impressions that would bring an entity below this threshold are treated as high-value (growing reach). When omitted, the seller uses their platform default (typically 1).', + ge=1, + ), + ] + window: Annotated[ + duration.Duration, + Field( + description='Time window over which frequency is measured (e.g. {"interval": 7, "unit": "days"} or {"interval": 1, "unit": "campaign"} for the full flight). Weekly windows are typical for brand campaigns; daily windows suit high-cadence direct response.' + ), + ] + + +class TargetFrequency1(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + max: Annotated[ + int, + Field( + description='Frequency at which an entity is considered saturated within the window. Impressions toward entities at or above this threshold are treated as lower-value. When both min and max are present, max must be greater than or equal to min. When omitted, the seller determines the saturation point.', + ge=1, + ), + ] + min: Annotated[ + int | None, + Field( + description='Minimum frequency for an entity to be considered meaningfully reached within the window. Impressions that would bring an entity below this threshold are treated as high-value (growing reach). When omitted, the seller uses their platform default (typically 1).', + ge=1, + ), + ] = None + window: Annotated[ + duration.Duration, + Field( + description='Time window over which frequency is measured (e.g. {"interval": 7, "unit": "days"} or {"interval": 1, "unit": "campaign"} for the full flight). Weekly windows are typical for brand campaigns; daily windows suit high-cadence direct response.' + ), + ] class AttributionWindow(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - click_through: Annotated[ - str, Field(description="Click-through attribution window (e.g. '7d', '28d', '30d')") + post_click: Annotated[ + duration.Duration, + Field( + description='Post-click attribution window. Conversions within this duration after a click are attributed to the ad (e.g. {"interval": 7, "unit": "days"}).' + ), ] - view_through: Annotated[ - str | None, Field(description="View-through attribution window (e.g. '1d', '7d')") + post_view: Annotated[ + duration.Duration | None, + Field( + description='Post-view attribution window. Conversions within this duration after an ad impression (without click) are attributed to the ad (e.g. {"interval": 1, "unit": "days"}).' + ), ] = None -class OptimizationGoal(AdCPBaseModel): +class EventSource(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - attribution_window: Annotated[ - AttributionWindow | None, + custom_event_name: Annotated[ + str | None, Field( - description="Attribution window for this optimization goal. Values must match an option declared in the seller's conversion_tracking.attribution_windows capability. When omitted, the seller uses their default window." + description="Required when event_type is 'custom'. Platform-specific name for the custom event." ), ] = None event_source_id: Annotated[ str, Field( - description='Event source to optimize against (must be configured on this account via sync_event_sources)' + description='Event source to include (must be configured on this account via sync_event_sources)', + min_length=1, ), ] event_type: Annotated[ event_type_1.EventType, - Field(description='Event type to optimize for (e.g. purchase, lead)'), + Field( + description='Event type to include from this source (e.g., purchase, lead, app_install, refund)' + ), ] - target_cpa: Annotated[ + value_factor: Annotated[ float | None, Field( - description='Target cost per acquisition in the buy currency. Mutually exclusive with target_roas.', - gt=0.0, + description="Multiplier the seller must apply to value_field before aggregation. Use -1 for refund events (negate the value), 0.01 for values in cents, -0.01 for refunds in cents. A value of 0 zeroes out this source's value contribution (the source still counts for event dedup). Defaults to 1. This is not passed as a parameter to underlying platform APIs — the seller applies it when computing aggregated value metrics." + ), + ] = 1 + value_field: Annotated[ + str | None, + Field( + description="Which field in the event's custom_data carries the monetary value. The seller must use this field for value extraction and aggregation when computing ROAS and conversion value metrics. Required on at least one entry when target.kind is 'per_ad_spend' or 'maximize_value'. Common values: 'value', 'order_total', 'profit_margin'. This is not passed as a parameter to underlying platform APIs — the seller maps it to their platform's value ingestion mechanism." + ), + ] = None + + +class Target2(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + kind: Literal['cost_per'] + value: Annotated[float, Field(description='Target cost per event in the buy currency', gt=0.0)] + + +class Target3(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + kind: Literal['per_ad_spend'] + value: Annotated[ + float, + Field(description='Target return ratio (e.g., 4.0 means $4 of value per $1 spent)', gt=0.0), + ] + + +class Target4(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + kind: Literal['maximize_value'] + + +class OptimizationGoal2(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + attribution_window: Annotated[ + AttributionWindow | None, + Field( + description="Attribution window for this optimization goal. Values must match an option declared in the seller's conversion_tracking.attribution_windows capability. Sellers must reject windows not in their declared capabilities. When omitted, the seller uses their default window." + ), + ] = None + event_sources: Annotated[ + list[EventSource], + Field( + description='Event source and type pairs that feed this goal. Each entry identifies a source and event type to include. When the seller supports multi_source_event_dedup (declared in get_adcp_capabilities), they deduplicate by event_id across all entries — the same business event from multiple sources counts once, using value_field and value_factor from the first matching entry. When multi_source_event_dedup is false or absent, buyers should use a single entry per goal; the seller will use only the first entry. All event sources must be configured via sync_event_sources.', + min_length=1, + ), + ] + kind: Literal['event'] + priority: Annotated[ + int | None, + Field( + description='Relative priority among all optimization goals on this package. 1 = highest priority (primary goal); higher numbers are lower priority (secondary signals). When omitted, sellers may use array position as priority.', + ge=1, + ), + ] = None + target: Annotated[ + Target2 | Target3 | Target4 | None, + Field( + description='Target cost or return for this event goal. When omitted, the seller optimizes for maximum conversions within budget.' + ), + ] = None + + +class OptimizationGoal1(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + kind: Literal['metric'] + metric: Annotated[ + Metric, + Field( + description="Seller-native metric to optimize for. Delivery metrics: clicks (link clicks, swipe-throughs, CTA taps that navigate away), views (viewable impressions), completed_views (video/audio completions — see view_duration_seconds), reach (unique audience reach — see reach_unit and target_frequency). Duration/score metrics: viewed_seconds (time in view per impression), attention_seconds (attention time per impression), attention_score (vendor-specific attention score). Audience action metrics: engagements (any direct interaction with the ad unit beyond viewing — social reactions/comments/shares, story/unit opens, interactive overlay taps, companion banner interactions on audio and CTV), follows (new followers, page likes, artist/podcast/channel subscribes), saves (saves, bookmarks, playlist adds, pins — signals of intent to return), profile_visits (visits to the brand's in-platform page — profile, artist page, channel, or storefront. Does not include external website clicks, which are covered by 'clicks')." + ), + ] + priority: Annotated[ + int | None, + Field( + description='Relative priority among all optimization goals on this package. 1 = highest priority (primary goal); higher numbers are lower priority (secondary signals). When omitted, sellers may use array position as priority.', + ge=1, ), ] = None - target_roas: Annotated[ + reach_unit: Annotated[ + reach_unit_1.ReachUnit | None, + Field( + description="Unit for reach measurement. Required when metric is 'reach'. Must be a value declared in the product's metric_optimization.supported_reach_units." + ), + ] = None + target: Annotated[ + Target | Target1 | None, + Field( + description='Target for this metric. When omitted, the seller optimizes for maximum metric volume within budget.' + ), + ] = None + target_frequency: Annotated[ + TargetFrequency | TargetFrequency1 | None, + Field( + description="Target frequency band for reach optimization. Only applicable when metric is 'reach'. Frames frequency as an optimization signal: the seller should treat impressions toward entities already within the [min, max] band as lower-value, and impressions toward unreached entities as higher-value. This shifts budget toward fresh reach rather than re-reaching known users. When omitted, the seller maximizes unique reach without a frequency constraint. A hard cap can still be layered via targeting_overlay.frequency_cap if a ceiling is needed." + ), + ] = None + view_duration_seconds: Annotated[ float | None, Field( - description='Target return on ad spend (e.g. 4.0 = $4 conversion value per $1 spent). Mutually exclusive with target_cpa.', + description="Minimum video view duration in seconds that qualifies as a completed_view for this goal. Only applicable when metric is 'completed_views'. When omitted, the seller uses their platform default (typically 2–15 seconds). Common values: 2 (Snap/LinkedIn default), 6 (TikTok), 15 (Snap 15-second views, Meta ThruPlay). Sellers declare which durations they support in metric_optimization.supported_view_durations. Sellers must reject goals with unsupported values — silent rounding would create measurement discrepancies.", gt=0.0, ), ] = None + + +class OptimizationGoal(RootModel[OptimizationGoal1 | OptimizationGoal2]): + root: Annotated[ + OptimizationGoal1 | OptimizationGoal2, + Field( + description='A single optimization target for a package. Packages accept an array of optimization_goals. When multiple goals are present, priority determines which the seller focuses on — 1 is highest priority (primary goal); higher numbers are secondary. Duplicate priority values result in undefined seller behavior.', + title='Optimization Goal', + ), + ] diff --git a/src/adcp/types/generated_poc/core/measurement.py b/src/adcp/types/generated_poc/core/outcome_measurement.py similarity index 72% rename from src/adcp/types/generated_poc/core/measurement.py rename to src/adcp/types/generated_poc/core/outcome_measurement.py index 72598831..5ed2dd29 100644 --- a/src/adcp/types/generated_poc/core/measurement.py +++ b/src/adcp/types/generated_poc/core/outcome_measurement.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: -# filename: core/measurement.json -# timestamp: 2026-02-23T01:56:40+00:00 +# filename: core/outcome_measurement.json +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,8 +9,10 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field +from . import duration -class Measurement(AdCPBaseModel): + +class OutcomeMeasurement(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) @@ -36,5 +38,8 @@ class Measurement(AdCPBaseModel): ), ] window: Annotated[ - str | None, Field(description='Attribution window', examples=['30_days', '7_days']) + duration.Duration | None, + Field( + description='Attribution window as a structured duration (e.g., {"interval": 30, "unit": "days"}).' + ), ] = None diff --git a/src/adcp/types/generated_poc/core/overlay.py b/src/adcp/types/generated_poc/core/overlay.py new file mode 100644 index 00000000..132c8dc6 --- /dev/null +++ b/src/adcp/types/generated_poc/core/overlay.py @@ -0,0 +1,84 @@ +# generated by datamodel-codegen: +# filename: core/overlay.json +# timestamp: 2026-02-27T02:10:10+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import AnyUrl, ConfigDict, Field + + +class Unit(Enum): + px = 'px' + fraction = 'fraction' + + +class Bounds(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + height: Annotated[float, Field(description='Height of the overlay', ge=0.0)] + unit: Annotated[ + Unit, + Field( + description="'px' = absolute pixels from asset top-left. 'fraction' = proportional to asset dimensions (x/y: 0.0 = asset edge, 1.0 = opposite edge; width/height: 0.12 = 12% of asset dimension)." + ), + ] + width: Annotated[float, Field(description='Width of the overlay', ge=0.0)] + x: Annotated[float, Field(description="Horizontal offset from the asset's left edge")] + y: Annotated[float, Field(description="Vertical offset from the asset's top edge")] + + +class Visual(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + dark: Annotated[ + AnyUrl | None, + Field(description='URL to the overlay graphic for use on dark backgrounds (SVG or PNG)'), + ] = None + light: Annotated[ + AnyUrl | None, + Field( + description='URL to the overlay graphic for use on light/bright backgrounds (SVG or PNG)' + ), + ] = None + url: Annotated[ + AnyUrl | None, + Field( + description='URL to a theme-neutral overlay graphic (SVG or PNG). Use when a single file works for all backgrounds, e.g. an SVG using CSS custom properties or currentColor.' + ), + ] = None + + +class Overlay(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + bounds: Annotated[ + Bounds, + Field( + description="Position and size of the overlay relative to the asset's own top-left corner. See 'unit' for coordinate interpretation." + ), + ] + description: Annotated[ + str | None, + Field( + description='Human-readable explanation of what this overlay is and how buyers should account for it' + ), + ] = None + id: Annotated[ + str, + Field( + description="Identifier for this overlay (e.g., 'play_pause', 'volume', 'publisher_logo', 'carousel_prev', 'carousel_next')" + ), + ] + visual: Annotated[ + Visual | None, + Field( + description='Optional visual reference for this overlay element. Useful for creative agents compositing previews and for buyers understanding what will appear over their content. Must include at least one of: url, light, or dark.' + ), + ] = None diff --git a/src/adcp/types/generated_poc/core/package.py b/src/adcp/types/generated_poc/core/package.py index 4c83c1a4..37a90909 100644 --- a/src/adcp/types/generated_poc/core/package.py +++ b/src/adcp/types/generated_poc/core/package.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/package.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,11 +10,9 @@ from pydantic import ConfigDict, Field from ..enums import pacing as pacing_1 -from . import creative_assignment +from . import catalog, creative_assignment from . import ext as ext_1 -from . import format_id -from . import optimization_goal as optimization_goal_1 -from . import targeting +from . import format_id, optimization_goal, targeting class Package(AdCPBaseModel): @@ -38,11 +36,23 @@ class Package(AdCPBaseModel): buyer_ref: Annotated[ str | None, Field(description="Buyer's reference identifier for this package") ] = None + catalogs: Annotated[ + list[catalog.Catalog] | None, + Field( + description='Catalogs this package promotes. Each catalog MUST have a distinct type (e.g., one product catalog, one store catalog). This constraint is enforced at the application level — sellers MUST reject requests containing multiple catalogs of the same type with a validation_error. Echoed from the create_media_buy request.' + ), + ] = None creative_assignments: Annotated[ list[creative_assignment.CreativeAssignment] | None, Field(description='Creative assets assigned to this package'), ] = None ext: ext_1.ExtensionObject | None = None + format_ids: Annotated[ + list[format_id.FormatId] | None, + Field( + description='Format IDs active for this package. Echoed from the create_media_buy request; omitted means all formats for the product are active.' + ), + ] = None format_ids_to_provide: Annotated[ list[format_id.FormatId] | None, Field(description='Format IDs that creative assets will be provided for this package'), @@ -50,7 +60,13 @@ class Package(AdCPBaseModel): impressions: Annotated[ float | None, Field(description='Impression goal for this package', ge=0.0) ] = None - optimization_goal: optimization_goal_1.OptimizationGoal | None = None + optimization_goals: Annotated[ + list[optimization_goal.OptimizationGoal] | None, + Field( + description='Optimization targets for this package. The seller optimizes delivery toward these goals in priority order. Common pattern: event goals (purchase, install) as primary targets at priority 1; metric goals (clicks, views) as secondary proxy signals at priority 2+.', + min_length=1, + ), + ] = None pacing: pacing_1.Pacing | None = None package_id: Annotated[str, Field(description="Publisher's unique identifier for the package")] paused: Annotated[ diff --git a/src/adcp/types/generated_poc/core/product.py b/src/adcp/types/generated_poc/core/product.py index 444cc5ae..a4ac68ca 100644 --- a/src/adcp/types/generated_poc/core/product.py +++ b/src/adcp/types/generated_poc/core/product.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/product.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -13,11 +13,12 @@ from ..enums import action_source, catalog_type from ..enums import channels as channels_1 from ..enums import delivery_type as delivery_type_1 +from ..enums import reach_unit from . import creative_policy as creative_policy_1 from . import data_provider_signal_selector, delivery_forecast from . import ext as ext_1 from . import format_id as format_id_1 -from . import measurement as measurement_1 +from . import outcome_measurement as outcome_measurement_1 from . import placement, pricing_option, publisher_property_selector from . import reporting_capabilities as reporting_capabilities_1 @@ -48,10 +49,10 @@ class CatalogMatch(AdCPBaseModel): ] -class SupportedOptimizationStrategy(Enum): - maximize_conversions = 'maximize_conversions' - target_cpa = 'target_cpa' - target_roas = 'target_roas' +class SupportedTarget(Enum): + cost_per = 'cost_per' + per_ad_spend = 'per_ad_spend' + maximize_value = 'maximize_value' class ConversionTracking(AdCPBaseModel): @@ -71,10 +72,10 @@ class ConversionTracking(AdCPBaseModel): description="Whether the seller provides its own always-on measurement (e.g. Amazon sales attribution for Amazon advertisers). When true, sync_event_sources response will include seller-managed event sources with managed_by='seller'." ), ] = None - supported_optimization_strategies: Annotated[ - list[SupportedOptimizationStrategy] | None, + supported_targets: Annotated[ + list[SupportedTarget] | None, Field( - description='Optimization strategies this product supports when an optimization_goal is set on a package', + description='Target kinds available for event goals on this product. Values match target.kind on the optimization goal. cost_per: target cost per conversion event. per_ad_spend: target return on ad spend (requires value_field on event sources). maximize_value: maximize total conversion value without a specific ratio target (requires value_field). Only these target kinds are accepted — goals with unlisted target kinds will be rejected. A goal without a target implicitly maximizes conversion count within budget — no declaration needed for that mode. When omitted, buyers can still set target-less event goals.', min_length=1, ), ] = None @@ -95,6 +96,29 @@ class DeliveryMeasurement(AdCPBaseModel): ] +class SupportedMetric(Enum): + clicks = 'clicks' + views = 'views' + completed_views = 'completed_views' + viewed_seconds = 'viewed_seconds' + attention_seconds = 'attention_seconds' + attention_score = 'attention_score' + engagements = 'engagements' + follows = 'follows' + saves = 'saves' + profile_visits = 'profile_visits' + reach = 'reach' + + +class SupportedTarget1(Enum): + cost_per = 'cost_per' + threshold_rate = 'threshold_rate' + + +class SupportedViewDuration(RootModel[float]): + root: Annotated[float, Field(gt=0.0)] + + class ProductCard(AdCPBaseModel): model_config = ConfigDict( extra='allow', @@ -129,6 +153,38 @@ class ProductCardDetailed(AdCPBaseModel): ] +class MetricOptimization(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + supported_metrics: Annotated[ + list[SupportedMetric], + Field( + description='Metric kinds this product can optimize for. Buyers should only request metric goals for kinds listed here.', + min_length=1, + ), + ] + supported_reach_units: Annotated[ + list[reach_unit.ReachUnit] | None, + Field( + description="Reach units this product can optimize for. Required when supported_metrics includes 'reach'. Buyers must set reach_unit to a value in this list on reach optimization goals — sellers reject unsupported values.", + min_length=1, + ), + ] = None + supported_targets: Annotated[ + list[SupportedTarget1] | None, + Field( + description='Target kinds available for metric goals on this product. Values match target.kind on the optimization goal. Only these target kinds are accepted — goals with unlisted target kinds will be rejected. When omitted, buyers can set target-less metric goals (maximize volume within budget) but cannot set specific targets.' + ), + ] = None + supported_view_durations: Annotated[ + list[SupportedViewDuration] | None, + Field( + description="Video view duration thresholds (in seconds) this product supports for completed_views goals. Only relevant when supported_metrics includes 'completed_views'. When absent, the seller uses their platform default. Buyers must set view_duration_seconds to a value in this list — sellers reject unsupported values." + ), + ] = None + + class Product(AdCPBaseModel): model_config = ConfigDict( extra='allow', @@ -161,7 +217,7 @@ class Product(AdCPBaseModel): conversion_tracking: Annotated[ ConversionTracking | None, Field( - description='Conversion tracking for this product. Presence indicates the product supports conversion-optimized delivery. Seller-level capabilities (supported event types, UID types, attribution windows) are declared in get_adcp_capabilities.' + description="Conversion event tracking for this product. Presence indicates the product supports optimization_goals with kind: 'event'. Seller-level capabilities (supported event types, UID types, attribution windows) are declared in get_adcp_capabilities." ), ] = None creative_policy: creative_policy_1.CreativePolicy | None = None @@ -198,8 +254,21 @@ class Product(AdCPBaseModel): ), ] is_custom: Annotated[bool | None, Field(description='Whether this is a custom product')] = None - measurement: measurement_1.Measurement | None = None + max_optimization_goals: Annotated[ + int | None, + Field( + description='Maximum number of optimization_goals this product accepts on a package. When absent, no limit is declared. Most social platforms accept only 1 goal — buyers sending arrays longer than this value should expect the seller to use only the highest-priority (lowest priority number) goal.', + ge=1, + ), + ] = None + metric_optimization: Annotated[ + MetricOptimization | None, + Field( + description="Metric optimization capabilities for this product. Presence indicates the product supports optimization_goals with kind: 'metric'. No event source or conversion tracking setup required — the seller tracks these metrics natively." + ), + ] = None name: Annotated[str, Field(description='Human-readable product name')] + outcome_measurement: outcome_measurement_1.OutcomeMeasurement | None = None placements: Annotated[ list[placement.Placement] | None, Field( diff --git a/src/adcp/types/generated_poc/core/product_filters.py b/src/adcp/types/generated_poc/core/product_filters.py index 69633846..5eba584c 100644 --- a/src/adcp/types/generated_poc/core/product_filters.py +++ b/src/adcp/types/generated_poc/core/product_filters.py @@ -1,10 +1,10 @@ # generated by datamodel-codegen: # filename: core/product_filters.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations -from datetime import date +from datetime import date as date_aliased from typing import Annotated from adcp.types.base import AdCPBaseModel @@ -105,7 +105,7 @@ class ProductFilters(AdCPBaseModel): ] = None delivery_type: delivery_type_1.DeliveryType | None = None end_date: Annotated[ - date | None, + date_aliased | None, Field( description='Campaign end date (ISO 8601 date format: YYYY-MM-DD) for availability checks' ), @@ -172,7 +172,7 @@ class ProductFilters(AdCPBaseModel): bool | None, Field(description='Only return products accepting IAB standard formats') ] = None start_date: Annotated[ - date | None, + date_aliased | None, Field( description='Campaign start date (ISO 8601 date format: YYYY-MM-DD) for availability checks' ), diff --git a/src/adcp/types/generated_poc/core/provenance.py b/src/adcp/types/generated_poc/core/provenance.py new file mode 100644 index 00000000..f9b788f6 --- /dev/null +++ b/src/adcp/types/generated_poc/core/provenance.py @@ -0,0 +1,194 @@ +# generated by datamodel-codegen: +# filename: core/provenance.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Annotated + +from adcp.types.base import AdCPBaseModel +from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field + +from ..enums import digital_source_type as digital_source_type_1 +from . import ext as ext_1 + + +class AiTool(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + name: Annotated[ + str, + Field( + description="Name of the AI tool or model (e.g., 'DALL-E 3', 'Stable Diffusion XL', 'Gemini')" + ), + ] + provider: Annotated[ + str | None, + Field( + description="Organization that provides the AI tool (e.g., 'OpenAI', 'Stability AI', 'Google')" + ), + ] = None + version: Annotated[str | None, Field(description='Version identifier for the AI tool')] = None + + +class C2pa(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + manifest_url: Annotated[ + AnyUrl, Field(description='URL to the C2PA manifest store for this content') + ] + + +class Role(Enum): + creator = 'creator' + advertiser = 'advertiser' + agency = 'agency' + platform = 'platform' + tool = 'tool' + + +class DeclaredBy(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + agent_url: Annotated[ + AnyUrl | None, + Field(description='URL of the agent or service that declared this provenance'), + ] = None + role: Annotated[Role, Field(description='Role of the declaring party in the supply chain')] + + +class Jurisdiction(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + country: Annotated[ + str, Field(description="ISO 3166-1 alpha-2 country code (e.g., 'US', 'DE', 'CN')") + ] + label_text: Annotated[ + str | None, + Field( + description='Required disclosure label text for this jurisdiction, in the local language' + ), + ] = None + region: Annotated[ + str | None, + Field(description="Sub-national region code (e.g., 'CA' for California, 'BY' for Bavaria)"), + ] = None + regulation: Annotated[ + str, + Field( + description="Regulation identifier (e.g., 'eu_ai_act_article_50', 'ca_sb_942', 'cn_deep_synthesis')" + ), + ] + + +class Disclosure(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + jurisdictions: Annotated[ + list[Jurisdiction] | None, + Field(description='Jurisdictions where disclosure obligations apply', min_length=1), + ] = None + required: Annotated[ + bool, + Field( + description='Whether AI disclosure is required for this content based on applicable regulations' + ), + ] + + +class HumanOversight(Enum): + none = 'none' + prompt_only = 'prompt_only' + selected = 'selected' + edited = 'edited' + directed = 'directed' + + +class Result(Enum): + authentic = 'authentic' + ai_generated = 'ai_generated' + ai_modified = 'ai_modified' + inconclusive = 'inconclusive' + + +class VerificationItem(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + confidence: Annotated[ + float | None, + Field( + description='Confidence score of the verification result (0.0 to 1.0)', ge=0.0, le=1.0 + ), + ] = None + details_url: Annotated[ + AnyUrl | None, Field(description='URL to the full verification report') + ] = None + result: Annotated[Result, Field(description='Verification outcome')] + verified_by: Annotated[ + str, + Field( + description="Name of the verification service (e.g., 'DoubleVerify', 'Hive Moderation', 'Reality Defender')" + ), + ] + verified_time: Annotated[ + AwareDatetime | None, Field(description='When the verification was performed (ISO 8601)') + ] = None + + +class Provenance(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + ai_tool: Annotated[ + AiTool | None, + Field( + description='AI system used to generate or modify this content. Aligns with IPTC 2025.1 AI metadata fields and C2PA claim_generator.' + ), + ] = None + c2pa: Annotated[ + C2pa | None, + Field( + description='C2PA Content Credentials reference. Links to the cryptographic provenance manifest for this content. Because file-level C2PA bindings break during ad-tech transcoding, this URL reference preserves the chain of provenance through the supply chain.' + ), + ] = None + created_time: Annotated[ + AwareDatetime | None, + Field(description='When this content was created or generated (ISO 8601)'), + ] = None + declared_by: Annotated[ + DeclaredBy | None, + Field( + description='Party declaring this provenance. Identifies who attached the provenance claim, enabling receiving parties to assess trust.' + ), + ] = None + digital_source_type: Annotated[ + digital_source_type_1.DigitalSourceType | None, + Field( + description='IPTC-aligned classification of AI involvement in producing this content' + ), + ] = None + disclosure: Annotated[ + Disclosure | None, + Field( + description='Regulatory disclosure requirements for this content. Indicates whether AI disclosure is required and under which jurisdictions.' + ), + ] = None + ext: ext_1.ExtensionObject | None = None + human_oversight: Annotated[ + HumanOversight | None, + Field(description='Level of human involvement in the AI-assisted creation process'), + ] = None + verification: Annotated[ + list[VerificationItem] | None, + Field( + description='Third-party verification or detection results for this content. Multiple services may independently evaluate the same content. Provenance is a claim — verification results attached by the declaring party are supplementary. The enforcing party (e.g., seller/publisher) should run its own verification via get_creative_features or calibrate_content.', + min_length=1, + ), + ] = None diff --git a/src/adcp/types/generated_poc/core/real_estate_item.py b/src/adcp/types/generated_poc/core/real_estate_item.py index a721a8b8..df1bcf02 100644 --- a/src/adcp/types/generated_poc/core/real_estate_item.py +++ b/src/adcp/types/generated_poc/core/real_estate_item.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/real_estate_item.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -11,6 +11,7 @@ from pydantic import AnyUrl, ConfigDict, Field from . import ext as ext_1 +from . import offering_asset_group from . import price as price_1 @@ -72,6 +73,13 @@ class RealEstateItem(AdCPBaseModel): ) address: Annotated[Address, Field(description='Property address.')] area: Annotated[Area | None, Field(description='Property size.')] = None + assets: Annotated[ + list[offering_asset_group.OfferingAssetGroup] | None, + Field( + description="Typed creative asset pools for this property listing. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (exterior/interior hero), 'images_vertical' (9:16 for Stories), 'images_square' (1:1). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + min_length=1, + ), + ] = None bathrooms: Annotated[ float | None, Field( diff --git a/src/adcp/types/generated_poc/core/reporting_capabilities.py b/src/adcp/types/generated_poc/core/reporting_capabilities.py index 27fd57a4..32eadd2a 100644 --- a/src/adcp/types/generated_poc/core/reporting_capabilities.py +++ b/src/adcp/types/generated_poc/core/reporting_capabilities.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/reporting_capabilities.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -11,6 +11,7 @@ from pydantic import ConfigDict, Field from ..enums import available_metric, reporting_frequency +from . import geo_breakdown_support class DateRangeSupport(Enum): @@ -50,12 +51,48 @@ class ReportingCapabilities(AdCPBaseModel): ge=0, ), ] + supports_audience_breakdown: Annotated[ + bool | None, + Field( + description='Whether this product supports audience segment breakdowns in delivery reporting (by_audience within by_package)' + ), + ] = None supports_creative_breakdown: Annotated[ bool | None, Field( description='Whether this product supports creative-level metric breakdowns in delivery reporting (by_creative within by_package)' ), ] = None + supports_device_platform_breakdown: Annotated[ + bool | None, + Field( + description='Whether this product supports device platform breakdowns in delivery reporting (by_device_platform within by_package)' + ), + ] = None + supports_device_type_breakdown: Annotated[ + bool | None, + Field( + description='Whether this product supports device type breakdowns in delivery reporting (by_device_type within by_package)' + ), + ] = None + supports_geo_breakdown: Annotated[ + geo_breakdown_support.GeographicBreakdownSupport | None, + Field( + description='Geographic breakdown support for this product. Declares which geo levels and systems are available for by_geo reporting within by_package.' + ), + ] = None + supports_keyword_breakdown: Annotated[ + bool | None, + Field( + description='Whether this product supports keyword-level metric breakdowns in delivery reporting (by_keyword within by_package)' + ), + ] = None + supports_placement_breakdown: Annotated[ + bool | None, + Field( + description='Whether this product supports placement breakdowns in delivery reporting (by_placement within by_package)' + ), + ] = None supports_webhooks: Annotated[ bool, Field(description='Whether this product supports webhook-based reporting notifications'), diff --git a/src/adcp/types/generated_poc/core/requirements/catalog_field_binding.py b/src/adcp/types/generated_poc/core/requirements/catalog_field_binding.py new file mode 100644 index 00000000..6f75b9d3 --- /dev/null +++ b/src/adcp/types/generated_poc/core/requirements/catalog_field_binding.py @@ -0,0 +1,138 @@ +# generated by datamodel-codegen: +# filename: core/requirements/catalog_field_binding.json +# timestamp: 2026-02-27T02:10:10+00:00 + +from __future__ import annotations + +from typing import Annotated, Literal + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field, RootModel + +from .. import ext as ext_1 + + +class AssetPoolBinding(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + asset_group_id: Annotated[ + str, + Field( + description="The asset_group_id on the catalog item's assets array to pull from (e.g., 'images_landscape', 'images_vertical', 'logo')." + ), + ] + asset_id: Annotated[ + str, + Field( + description="The asset_id from the format's assets array. Identifies which individual template slot this binding applies to." + ), + ] + ext: ext_1.ExtensionObject | None = None + kind: Literal['asset_pool'] + + +class ScalarBinding(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + asset_id: Annotated[ + str, + Field( + description="The asset_id from the format's assets array. Identifies which individual template slot this binding applies to." + ), + ] + catalog_field: Annotated[ + str, + Field( + description="Dot-notation path to the field on the catalog item (e.g., 'name', 'price.amount', 'location.city')." + ), + ] + ext: ext_1.ExtensionObject | None = None + kind: Literal['scalar'] + + +class CatalogFieldBinding1(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + catalog_item: Annotated[ + Literal[True], + Field( + description="Each repetition of the format's repeatable_group maps to one item from the catalog." + ), + ] + ext: ext_1.ExtensionObject | None = None + format_group_id: Annotated[ + str, + Field(description="The asset_group_id of a repeatable_group in the format's assets array."), + ] + kind: Literal['catalog_group'] + per_item_bindings: Annotated[ + list[ScalarBinding | AssetPoolBinding] | None, + Field( + description='Scalar and asset pool bindings that apply within each repetition of the group. Nested catalog_group bindings are not permitted.', + min_length=1, + ), + ] = None + + +class CatalogFieldBinding(RootModel[ScalarBinding | AssetPoolBinding | CatalogFieldBinding1]): + root: Annotated[ + ScalarBinding | AssetPoolBinding | CatalogFieldBinding1, + Field( + description="Maps a format template slot to a catalog item field or typed asset pool. The 'kind' field identifies the binding variant. All bindings are optional — agents can still infer mappings without them.", + examples=[ + { + 'data': {'asset_id': 'headline', 'catalog_field': 'name', 'kind': 'scalar'}, + 'description': 'Scalar binding — hotel name to headline slot', + }, + { + 'data': { + 'asset_id': 'price_badge', + 'catalog_field': 'price.amount', + 'kind': 'scalar', + }, + 'description': 'Scalar binding — nested field (nightly rate)', + }, + { + 'data': { + 'asset_group_id': 'images_landscape', + 'asset_id': 'hero_image', + 'kind': 'asset_pool', + }, + 'description': 'Asset pool binding — hero image from landscape pool', + }, + { + 'data': { + 'asset_group_id': 'images_vertical', + 'asset_id': 'snap_background', + 'kind': 'asset_pool', + }, + 'description': 'Asset pool binding — Snap vertical background from vertical pool', + }, + { + 'data': { + 'catalog_item': True, + 'format_group_id': 'slide', + 'kind': 'catalog_group', + 'per_item_bindings': [ + {'asset_id': 'title', 'catalog_field': 'name', 'kind': 'scalar'}, + { + 'asset_id': 'price', + 'catalog_field': 'price.amount', + 'kind': 'scalar', + }, + { + 'asset_group_id': 'images_landscape', + 'asset_id': 'image', + 'kind': 'asset_pool', + }, + ], + }, + 'description': 'Catalog group binding — carousel where each slide is one hotel', + }, + ], + title='Catalog Field Binding', + ), + ] diff --git a/src/adcp/types/generated_poc/core/requirements/catalog_requirements.py b/src/adcp/types/generated_poc/core/requirements/catalog_requirements.py index 313c704e..859c3a5b 100644 --- a/src/adcp/types/generated_poc/core/requirements/catalog_requirements.py +++ b/src/adcp/types/generated_poc/core/requirements/catalog_requirements.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/requirements/catalog_requirements.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -11,7 +11,7 @@ from ...enums import catalog_type as catalog_type_1 from ...enums import feed_format -from . import offering_asset_constraint +from . import catalog_field_binding, offering_asset_constraint class CatalogRequirements(AdCPBaseModel): @@ -29,6 +29,20 @@ class CatalogRequirements(AdCPBaseModel): min_length=1, ), ] = None + field_bindings: Annotated[ + list[catalog_field_binding.CatalogFieldBinding] | None, + Field( + description='Explicit mappings from format template slots to catalog item fields or typed asset pools. Optional — creative agents can infer mappings without them, but bindings make the relationship self-describing and enable validation. Covers scalar fields (asset_id → catalog_field), asset pools (asset_id → asset_group_id on the catalog item), and repeatable groups that iterate over catalog items.', + min_length=1, + ), + ] = None + max_items: Annotated[ + int | None, + Field( + description='Maximum number of items the format can render. Items beyond this limit are ignored. Useful for fixed-slot layouts (e.g., a 3-product card) or feed-size constraints.', + ge=1, + ), + ] = None min_items: Annotated[ int | None, Field( @@ -39,7 +53,7 @@ class CatalogRequirements(AdCPBaseModel): offering_asset_constraints: Annotated[ list[offering_asset_constraint.OfferingAssetConstraint] | None, Field( - description="Per-offering creative requirements. Only applicable when catalog_type is 'offering'. Declares what asset groups (headlines, images, videos) each offering must provide, along with count bounds and per-asset technical constraints.", + description="Per-item creative asset requirements. Declares what asset groups (headlines, images, videos) each catalog item must provide in its assets array, along with count bounds and per-asset technical constraints. Applicable to 'offering' and all vertical catalog types (hotel, flight, job, etc.) whose items carry typed assets.", min_length=1, ), ] = None diff --git a/src/adcp/types/generated_poc/core/signal_filters.py b/src/adcp/types/generated_poc/core/signal_filters.py index 2cd9b8b4..876f08e8 100644 --- a/src/adcp/types/generated_poc/core/signal_filters.py +++ b/src/adcp/types/generated_poc/core/signal_filters.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/signal_filters.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -23,7 +23,18 @@ class SignalFilters(AdCPBaseModel): data_providers: Annotated[ list[str] | None, Field(description='Filter by specific data providers', min_length=1) ] = None - max_cpm: Annotated[float | None, Field(description='Maximum CPM price filter', ge=0.0)] = None + max_cpm: Annotated[ + float | None, + Field(description="Maximum CPM filter. Applies only to signals with model='cpm'.", ge=0.0), + ] = None + max_percent: Annotated[ + float | None, + Field( + description='Maximum percent-of-media rate filter. Signals where all percent_of_media pricing options exceed this value are excluded. Does not account for max_cpm caps.', + ge=0.0, + le=100.0, + ), + ] = None min_coverage_percentage: Annotated[ float | None, Field(description='Minimum coverage requirement', ge=0.0, le=100.0) ] = None diff --git a/src/adcp/types/generated_poc/core/signal_pricing.py b/src/adcp/types/generated_poc/core/signal_pricing.py new file mode 100644 index 00000000..b03b0965 --- /dev/null +++ b/src/adcp/types/generated_poc/core/signal_pricing.py @@ -0,0 +1,73 @@ +# generated by datamodel-codegen: +# filename: core/signal_pricing.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Annotated, Literal + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field, RootModel + +from . import ext as ext_1 + + +class SignalPricing1(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + cpm: Annotated[float, Field(description='Cost per thousand impressions', ge=0.0)] + currency: Annotated[str, Field(description='ISO 4217 currency code', pattern='^[A-Z]{3}$')] + ext: ext_1.ExtensionObject | None = None + model: Literal['cpm'] + + +class SignalPricing2(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + currency: Annotated[ + str, + Field(description='ISO 4217 currency code for the resulting charge', pattern='^[A-Z]{3}$'), + ] + ext: ext_1.ExtensionObject | None = None + max_cpm: Annotated[ + float | None, + Field( + description='Optional CPM cap. When set, the effective charge is min(percent × media_spend_per_mille, max_cpm).', + ge=0.0, + ), + ] = None + model: Literal['percent_of_media'] + percent: Annotated[ + float, Field(description='Percentage of media spend, e.g. 15 = 15%', ge=0.0, le=100.0) + ] + + +class Period(Enum): + monthly = 'monthly' + quarterly = 'quarterly' + annual = 'annual' + campaign = 'campaign' + + +class SignalPricing3(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + amount: Annotated[float, Field(description='Fixed charge for the billing period', ge=0.0)] + currency: Annotated[str, Field(description='ISO 4217 currency code', pattern='^[A-Z]{3}$')] + ext: ext_1.ExtensionObject | None = None + model: Literal['flat_fee'] + period: Annotated[Period, Field(description='Billing period for the flat fee.')] + + +class SignalPricing(RootModel[SignalPricing1 | SignalPricing2 | SignalPricing3]): + root: Annotated[ + SignalPricing1 | SignalPricing2 | SignalPricing3, + Field( + description="Pricing model for a signal. Discriminated by model: 'cpm' (fixed CPM), 'percent_of_media' (percentage of spend with optional CPM cap), or 'flat_fee' (fixed charge per reporting period, e.g. monthly licensed segments).", + title='Signal Pricing', + ), + ] diff --git a/src/adcp/types/generated_poc/core/signal_pricing_option.py b/src/adcp/types/generated_poc/core/signal_pricing_option.py new file mode 100644 index 00000000..300e4e7b --- /dev/null +++ b/src/adcp/types/generated_poc/core/signal_pricing_option.py @@ -0,0 +1,96 @@ +# generated by datamodel-codegen: +# filename: core/signal_pricing_option.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum +from typing import Annotated, Literal + +from adcp.types.base import AdCPBaseModel +from pydantic import ConfigDict, Field, RootModel + +from . import ext as ext_1 + + +class SignalPricingOption1(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + cpm: Annotated[float, Field(description='Cost per thousand impressions', ge=0.0)] + currency: Annotated[str, Field(description='ISO 4217 currency code', pattern='^[A-Z]{3}$')] + ext: ext_1.ExtensionObject | None = None + model: Literal['cpm'] + + +class SignalPricingOption2(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + currency: Annotated[ + str, + Field(description='ISO 4217 currency code for the resulting charge', pattern='^[A-Z]{3}$'), + ] + ext: ext_1.ExtensionObject | None = None + max_cpm: Annotated[ + float | None, + Field( + description='Optional CPM cap. When set, the effective charge is min(percent × media_spend_per_mille, max_cpm).', + ge=0.0, + ), + ] = None + model: Literal['percent_of_media'] + percent: Annotated[ + float, Field(description='Percentage of media spend, e.g. 15 = 15%', ge=0.0, le=100.0) + ] + + +class Period(Enum): + monthly = 'monthly' + quarterly = 'quarterly' + annual = 'annual' + campaign = 'campaign' + + +class SignalPricingOption3(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + amount: Annotated[float, Field(description='Fixed charge for the billing period', ge=0.0)] + currency: Annotated[str, Field(description='ISO 4217 currency code', pattern='^[A-Z]{3}$')] + ext: ext_1.ExtensionObject | None = None + model: Literal['flat_fee'] + period: Annotated[Period, Field(description='Billing period for the flat fee.')] + + +class SignalPricingOption4(AdCPBaseModel): + pricing_option_id: Annotated[ + str, + Field( + description='Opaque identifier for this pricing option, unique within the signals agent. Pass this in report_usage to identify which pricing option was applied.' + ), + ] + + +class SignalPricingOption5(SignalPricingOption1, SignalPricingOption4): + pass + + +class SignalPricingOption6(SignalPricingOption2, SignalPricingOption4): + pass + + +class SignalPricingOption7(SignalPricingOption3, SignalPricingOption4): + pass + + +class SignalPricingOption( + RootModel[SignalPricingOption5 | SignalPricingOption6 | SignalPricingOption7] +): + root: Annotated[ + SignalPricingOption5 | SignalPricingOption6 | SignalPricingOption7, + Field( + description='A pricing option offered by a signals agent. Combines pricing_option_id with the signal pricing model fields at the same level — pass pricing_option_id in report_usage for billing verification.', + title='Signal Pricing Option', + ), + ] diff --git a/src/adcp/types/generated_poc/core/targeting.py b/src/adcp/types/generated_poc/core/targeting.py index 9f74894f..2e1d8973 100644 --- a/src/adcp/types/generated_poc/core/targeting.py +++ b/src/adcp/types/generated_poc/core/targeting.py @@ -1,18 +1,22 @@ # generated by datamodel-codegen: # filename: core/targeting.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations -from typing import Annotated +from enum import Enum +from typing import Annotated, Any from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field, RootModel from ..enums import age_verification_method from ..enums import device_platform as device_platform_1 -from ..enums import metro_system, postal_system +from ..enums import device_type as device_type_1 +from ..enums import distance_unit, metro_system, postal_system +from ..enums import transport_mode as transport_mode_1 from . import daypart_target +from . import ext as ext_1 from . import frequency_cap as frequency_cap_1 from . import property_list_ref @@ -115,6 +119,61 @@ class GeoPostalAreasExcludeItem(AdCPBaseModel): ] +class Type(Enum): + Polygon = 'Polygon' + MultiPolygon = 'MultiPolygon' + + +class Geometry(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + coordinates: Annotated[ + list[Any], + Field( + description='GeoJSON coordinates array. For Polygon: array of linear rings. For MultiPolygon: array of polygons.' + ), + ] + type: Annotated[Type, Field(description='GeoJSON geometry type.')] + + +class Radius(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + unit: Annotated[distance_unit.DistanceUnit, Field(description='Distance unit.')] + value: Annotated[float, Field(description='Radius distance.', gt=0.0)] + + +class Unit(Enum): + min = 'min' + hr = 'hr' + + +class TravelTime(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + unit: Annotated[Unit, Field(description='Time unit.')] + value: Annotated[float, Field(description='Travel time limit.', ge=1.0)] + + +class Geometry4(Geometry): + pass + + +class TravelTime4(TravelTime): + pass + + +class Geometry5(Geometry): + pass + + +class TravelTime5(TravelTime): + pass + + class GeoRegion(RootModel[str]): root: Annotated[str, Field(pattern='^[A-Z]{2}-[A-Z0-9]{1,3}$')] @@ -123,10 +182,44 @@ class GeoRegionsExcludeItem(GeoRegion): pass +class MatchType(Enum): + broad = 'broad' + phrase = 'phrase' + exact = 'exact' + + +class KeywordTarget(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + bid_price: Annotated[ + float | None, + Field( + description="Per-keyword bid price, denominated in the same currency as the package's pricing option. Overrides the package-level bid_price for this keyword. Inherits the max_bid interpretation from the pricing option: when max_bid is true, this is the keyword's bid ceiling; when false, this is the exact bid. If omitted, the package bid_price applies.", + ge=0.0, + ), + ] = None + keyword: Annotated[str, Field(description='The keyword to target', min_length=1)] + match_type: Annotated[ + MatchType, + Field( + description='Match type: broad matches related queries, phrase matches queries containing the keyword phrase, exact matches the query exactly' + ), + ] + + class LanguageItem(RootModel[str]): root: Annotated[str, Field(pattern='^[a-z]{2}$')] +class NegativeKeyword(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + keyword: Annotated[str, Field(description='The keyword to exclude', min_length=1)] + match_type: Annotated[MatchType, Field(description='Match type for exclusion')] + + class StoreCatchment(AdCPBaseModel): model_config = ConfigDict( extra='allow', @@ -150,6 +243,165 @@ class StoreCatchment(AdCPBaseModel): ] = None +class GeoProximity(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + ext: ext_1.ExtensionObject | None = None + geometry: Annotated[ + Geometry | None, + Field( + description='Pre-computed GeoJSON geometry defining the proximity boundary. Use when the buyer has already calculated isochrones (via TravelTime, Mapbox, etc.) or has custom boundaries. When geometry is provided, lat/lng are not required.' + ), + ] = None + label: Annotated[ + str | None, + Field( + description="Human-readable label for this entry (e.g., 'Düsseldorf', 'Heathrow Airport', 'Primary trade area')." + ), + ] = None + lat: Annotated[ + float, + Field( + description='Latitude in decimal degrees (WGS 84). Required for travel_time and radius methods.', + ge=-90.0, + le=90.0, + ), + ] + lng: Annotated[ + float, + Field( + description='Longitude in decimal degrees (WGS 84). Required for travel_time and radius methods.', + ge=-180.0, + le=180.0, + ), + ] + radius: Annotated[ + Radius | None, + Field( + description='Simple radius from the point. The platform draws a circle of this distance around the coordinates.' + ), + ] = None + transport_mode: Annotated[ + transport_mode_1.TransportMode, + Field( + description='Transportation mode for isochrone calculation. Required when travel_time is provided.' + ), + ] + travel_time: Annotated[ + TravelTime, + Field( + description='Travel time limit for isochrone calculation. The platform resolves this to a geographic boundary based on actual transportation networks.' + ), + ] + + +class GeoProximity2(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + ext: ext_1.ExtensionObject | None = None + geometry: Annotated[ + Geometry4 | None, + Field( + description='Pre-computed GeoJSON geometry defining the proximity boundary. Use when the buyer has already calculated isochrones (via TravelTime, Mapbox, etc.) or has custom boundaries. When geometry is provided, lat/lng are not required.' + ), + ] = None + label: Annotated[ + str | None, + Field( + description="Human-readable label for this entry (e.g., 'Düsseldorf', 'Heathrow Airport', 'Primary trade area')." + ), + ] = None + lat: Annotated[ + float, + Field( + description='Latitude in decimal degrees (WGS 84). Required for travel_time and radius methods.', + ge=-90.0, + le=90.0, + ), + ] + lng: Annotated[ + float, + Field( + description='Longitude in decimal degrees (WGS 84). Required for travel_time and radius methods.', + ge=-180.0, + le=180.0, + ), + ] + radius: Annotated[ + Radius, + Field( + description='Simple radius from the point. The platform draws a circle of this distance around the coordinates.' + ), + ] + transport_mode: Annotated[ + transport_mode_1.TransportMode | None, + Field( + description='Transportation mode for isochrone calculation. Required when travel_time is provided.' + ), + ] = None + travel_time: Annotated[ + TravelTime4 | None, + Field( + description='Travel time limit for isochrone calculation. The platform resolves this to a geographic boundary based on actual transportation networks.' + ), + ] = None + + +class GeoProximity3(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + ext: ext_1.ExtensionObject | None = None + geometry: Annotated[ + Geometry5, + Field( + description='Pre-computed GeoJSON geometry defining the proximity boundary. Use when the buyer has already calculated isochrones (via TravelTime, Mapbox, etc.) or has custom boundaries. When geometry is provided, lat/lng are not required.' + ), + ] + label: Annotated[ + str | None, + Field( + description="Human-readable label for this entry (e.g., 'Düsseldorf', 'Heathrow Airport', 'Primary trade area')." + ), + ] = None + lat: Annotated[ + float | None, + Field( + description='Latitude in decimal degrees (WGS 84). Required for travel_time and radius methods.', + ge=-90.0, + le=90.0, + ), + ] = None + lng: Annotated[ + float | None, + Field( + description='Longitude in decimal degrees (WGS 84). Required for travel_time and radius methods.', + ge=-180.0, + le=180.0, + ), + ] = None + radius: Annotated[ + Radius | None, + Field( + description='Simple radius from the point. The platform draws a circle of this distance around the coordinates.' + ), + ] = None + transport_mode: Annotated[ + transport_mode_1.TransportMode | None, + Field( + description='Transportation mode for isochrone calculation. Required when travel_time is provided.' + ), + ] = None + travel_time: Annotated[ + TravelTime5 | None, + Field( + description='Travel time limit for isochrone calculation. The platform resolves this to a geographic boundary based on actual transportation networks.' + ), + ] = None + + class TargetingOverlay(AdCPBaseModel): model_config = ConfigDict( extra='allow', @@ -194,6 +446,20 @@ class TargetingOverlay(AdCPBaseModel): min_length=1, ), ] = None + device_type: Annotated[ + list[device_type_1.DeviceType] | None, + Field( + description='Restrict to specific device form factors. Use for campaigns targeting hardware categories rather than operating systems (e.g., mobile-only promotions, CTV campaigns).', + min_length=1, + ), + ] = None + device_type_exclude: Annotated[ + list[device_type_1.DeviceType] | None, + Field( + description='Exclude specific device form factors from delivery (e.g., exclude CTV for app-install campaigns).', + min_length=1, + ), + ] = None frequency_cap: frequency_cap_1.FrequencyCap | None = None geo_countries: Annotated[ list[GeoCountry] | None, @@ -237,6 +503,13 @@ class TargetingOverlay(AdCPBaseModel): min_length=1, ), ] = None + geo_proximity: Annotated[ + list[GeoProximity | GeoProximity2 | GeoProximity3] | None, + Field( + description='Target users within travel time, distance, or a custom boundary around arbitrary geographic points. Multiple entries use OR semantics — a user within range of any listed point is eligible. For campaigns targeting 10+ locations, consider using store_catchments with a location catalog instead. Seller must declare support in get_adcp_capabilities.', + min_length=1, + ), + ] = None geo_regions: Annotated[ list[GeoRegion] | None, Field( @@ -251,6 +524,13 @@ class TargetingOverlay(AdCPBaseModel): min_length=1, ), ] = None + keyword_targets: Annotated[ + list[KeywordTarget] | None, + Field( + description='Keyword targeting for search and retail media platforms. Restricts delivery to queries matching the specified keywords. Each keyword is identified by the tuple (keyword, match_type) — the same keyword string with different match types are distinct targets. Sellers SHOULD reject duplicate (keyword, match_type) pairs within a single request. Seller must declare support in get_adcp_capabilities.', + min_length=1, + ), + ] = None language: Annotated[ list[LanguageItem] | None, Field( @@ -258,6 +538,13 @@ class TargetingOverlay(AdCPBaseModel): min_length=1, ), ] = None + negative_keywords: Annotated[ + list[NegativeKeyword] | None, + Field( + description='Keywords to exclude from delivery. Queries matching these keywords will not trigger the ad. Each negative keyword is identified by the tuple (keyword, match_type). Seller must declare support in get_adcp_capabilities.', + min_length=1, + ), + ] = None property_list: Annotated[ property_list_ref.PropertyListReference | None, Field( diff --git a/src/adcp/types/generated_poc/core/vehicle_item.py b/src/adcp/types/generated_poc/core/vehicle_item.py index 4065c715..14d04cfa 100644 --- a/src/adcp/types/generated_poc/core/vehicle_item.py +++ b/src/adcp/types/generated_poc/core/vehicle_item.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: core/vehicle_item.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -11,6 +11,7 @@ from pydantic import AnyUrl, ConfigDict, Field from . import ext as ext_1 +from . import offering_asset_group from . import price as price_1 @@ -74,6 +75,13 @@ class VehicleItem(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) + assets: Annotated[ + list[offering_asset_group.OfferingAssetGroup] | None, + Field( + description="Typed creative asset pools for this vehicle. Uses the same OfferingAssetGroup structure as offering-type catalogs. Standard group IDs: 'images_landscape' (exterior hero), 'images_vertical' (9:16 for Stories), 'images_square' (1:1). Enables formats to declare typed image requirements that map unambiguously to the right asset regardless of platform.", + min_length=1, + ), + ] = None body_style: Annotated[BodyStyle | None, Field(description='Vehicle body style.')] = None condition: Annotated[Condition | None, Field(description='Vehicle condition.')] = None ext: ext_1.ExtensionObject | None = None diff --git a/src/adcp/types/generated_poc/creative/get_creative_delivery_request.py b/src/adcp/types/generated_poc/creative/get_creative_delivery_request.py index cebdd918..db54361f 100644 --- a/src/adcp/types/generated_poc/creative/get_creative_delivery_request.py +++ b/src/adcp/types/generated_poc/creative/get_creative_delivery_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: creative/get_creative_delivery_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,28 +9,20 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field, RootModel +from ..core import account_ref from ..core import context as context_1 from ..core import ext as ext_1 - - -class Pagination(AdCPBaseModel): - model_config = ConfigDict( - extra='allow', - ) - limit: Annotated[ - int | None, Field(description='Maximum number of creatives to return', ge=1, le=100) - ] = 50 - offset: Annotated[int | None, Field(description='Number of creatives to skip', ge=0)] = 0 +from ..core import pagination_request class GetCreativeDeliveryRequest1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_id: Annotated[ - str | None, + account: Annotated[ + account_ref.AccountReference | None, Field( - description='Account context for routing and scoping. Limits results to creatives within this account. Optional if the agent has a single account or can determine routing from the media buy identifiers.' + description='Account for routing and scoping. Limits results to creatives within this account.' ), ] = None context: context_1.ContextObject | None = None @@ -71,9 +63,9 @@ class GetCreativeDeliveryRequest1(AdCPBaseModel): ), ] pagination: Annotated[ - Pagination | None, + pagination_request.PaginationRequest | None, Field( - description='Pagination parameters for the creatives array in the response. When omitted, the agent returns all matching creatives.' + description='Pagination parameters for the creatives array in the response. Uses cursor-based pagination consistent with other list operations.' ), ] = None start_date: Annotated[ @@ -89,10 +81,10 @@ class GetCreativeDeliveryRequest2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_id: Annotated[ - str | None, + account: Annotated[ + account_ref.AccountReference | None, Field( - description='Account context for routing and scoping. Limits results to creatives within this account. Optional if the agent has a single account or can determine routing from the media buy identifiers.' + description='Account for routing and scoping. Limits results to creatives within this account.' ), ] = None context: context_1.ContextObject | None = None @@ -133,9 +125,9 @@ class GetCreativeDeliveryRequest2(AdCPBaseModel): ), ] = None pagination: Annotated[ - Pagination | None, + pagination_request.PaginationRequest | None, Field( - description='Pagination parameters for the creatives array in the response. When omitted, the agent returns all matching creatives.' + description='Pagination parameters for the creatives array in the response. Uses cursor-based pagination consistent with other list operations.' ), ] = None start_date: Annotated[ @@ -151,10 +143,10 @@ class GetCreativeDeliveryRequest3(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_id: Annotated[ - str | None, + account: Annotated[ + account_ref.AccountReference | None, Field( - description='Account context for routing and scoping. Limits results to creatives within this account. Optional if the agent has a single account or can determine routing from the media buy identifiers.' + description='Account for routing and scoping. Limits results to creatives within this account.' ), ] = None context: context_1.ContextObject | None = None @@ -195,9 +187,9 @@ class GetCreativeDeliveryRequest3(AdCPBaseModel): ), ] = None pagination: Annotated[ - Pagination | None, + pagination_request.PaginationRequest | None, Field( - description='Pagination parameters for the creatives array in the response. When omitted, the agent returns all matching creatives.' + description='Pagination parameters for the creatives array in the response. Uses cursor-based pagination consistent with other list operations.' ), ] = None start_date: Annotated[ diff --git a/src/adcp/types/generated_poc/creative/get_creative_features_request.py b/src/adcp/types/generated_poc/creative/get_creative_features_request.py index 16ff75aa..b7513c56 100644 --- a/src/adcp/types/generated_poc/creative/get_creative_features_request.py +++ b/src/adcp/types/generated_poc/creative/get_creative_features_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: creative/get_creative_features_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,6 +9,7 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field +from ..core import account_ref from ..core import context as context_1 from ..core import creative_manifest as creative_manifest_1 from ..core import ext as ext_1 @@ -16,8 +17,14 @@ class GetCreativeFeaturesRequest(AdCPBaseModel): model_config = ConfigDict( - extra='forbid', + extra='allow', ) + account: Annotated[ + account_ref.AccountReference | None, + Field( + description='Account for billing this evaluation. Required when the governance agent charges per evaluation.' + ), + ] = None context: context_1.ContextObject | None = None creative_manifest: Annotated[ creative_manifest_1.CreativeManifest, diff --git a/src/adcp/types/generated_poc/creative/get_creative_features_response.py b/src/adcp/types/generated_poc/creative/get_creative_features_response.py index fb76b531..f82b2b88 100644 --- a/src/adcp/types/generated_poc/creative/get_creative_features_response.py +++ b/src/adcp/types/generated_poc/creative/get_creative_features_response.py @@ -1,10 +1,10 @@ # generated by datamodel-codegen: # filename: creative/get_creative_features_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations -from typing import Annotated, Any +from typing import Annotated from adcp.types.base import AdCPBaseModel from pydantic import AnyUrl, Field, RootModel @@ -19,9 +19,6 @@ class GetCreativeFeaturesResponse2(AdCPBaseModel): context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None - results: Annotated[ - Any | None, Field(description='Field must not be present in error response') - ] = None class GetCreativeFeaturesResponse1(AdCPBaseModel): @@ -32,9 +29,6 @@ class GetCreativeFeaturesResponse1(AdCPBaseModel): description="URL to the vendor's full assessment report. The vendor controls what information is disclosed and access control." ), ] = None - errors: Annotated[ - Any | None, Field(description='Field must not be present in success response') - ] = None ext: ext_1.ExtensionObject | None = None results: Annotated[ list[creative_feature_result.CreativeFeatureResult], @@ -48,7 +42,7 @@ class GetCreativeFeaturesResponse( root: Annotated[ GetCreativeFeaturesResponse1 | GetCreativeFeaturesResponse2, Field( - description='Response payload for get_creative_features task. Returns feature values for the evaluated creative.', + description="Response payload for the get_creative_features task. Returns scored feature values from the governance agent's evaluation of the submitted creative manifest.", title='Get Creative Features Response', ), ] diff --git a/src/adcp/types/generated_poc/creative/preview_creative_request.py b/src/adcp/types/generated_poc/creative/preview_creative_request.py index 3af11985..92b7b81a 100644 --- a/src/adcp/types/generated_poc/creative/preview_creative_request.py +++ b/src/adcp/types/generated_poc/creative/preview_creative_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: creative/preview_creative_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -86,7 +86,7 @@ class PreviewCreativeRequest1(AdCPBaseModel): context: context_1.ContextObject | None = None creative_manifest: Annotated[ creative_manifest_1.CreativeManifest, - Field(description='Complete creative manifest with all required assets for the format'), + Field(description='Complete creative manifest with all required assets for the format.'), ] ext: ext_1.ExtensionObject | None = None format_id: Annotated[ @@ -123,7 +123,7 @@ class Request(AdCPBaseModel): ) creative_manifest: Annotated[ creative_manifest_1.CreativeManifest, - Field(description='Complete creative manifest with all required assets'), + Field(description='Complete creative manifest with all required assets.'), ] format_id: Annotated[ format_id_1.FormatId | None, diff --git a/src/adcp/types/generated_poc/creative/preview_creative_response.py b/src/adcp/types/generated_poc/creative/preview_creative_response.py index 52074418..d63ca6ec 100644 --- a/src/adcp/types/generated_poc/creative/preview_creative_response.py +++ b/src/adcp/types/generated_poc/creative/preview_creative_response.py @@ -1,16 +1,16 @@ # generated by datamodel-codegen: # filename: creative/preview_creative_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations -from typing import Annotated, Any, Literal +from typing import Annotated, Literal from adcp.types.base import AdCPBaseModel from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, RootModel from ..core import context as context_1 -from ..core import creative_manifest +from ..core import creative_manifest, error from ..core import ext as ext_1 from . import preview_render @@ -25,17 +25,6 @@ class Input(AdCPBaseModel): name: Annotated[str, Field(description='Human-readable name for this variant')] -class Error(AdCPBaseModel): - code: Annotated[ - str, - Field( - description="Error code (e.g., 'invalid_manifest', 'unsupported_format', 'missing_assets')" - ), - ] - details: Annotated[dict[str, Any] | None, Field(description='Additional error context')] = None - message: Annotated[str, Field(description='Human-readable error message')] - - class Input4(AdCPBaseModel): context_description: str | None = None macros: dict[str, str] | None = None @@ -94,7 +83,7 @@ class Preview1(AdCPBaseModel): class Response(AdCPBaseModel): - expires_at: AwareDatetime + expires_at: Annotated[AwareDatetime, Field(description='When the preview URLs expire')] interactive_url: AnyUrl | None = None previews: Annotated[ list[Preview1], @@ -103,11 +92,17 @@ class Response(AdCPBaseModel): class Results(AdCPBaseModel): - error: Annotated[Error | None, Field(description='Error information for failed requests')] = ( - None - ) - response: Annotated[Response, Field(description='Preview response for successful requests')] - success: Annotated[Literal[True], Field(description='Whether this preview request succeeded')] + creative_id: Annotated[ + str, + Field( + description='ID of the creative this result corresponds to. Enables correlation when processing batch results.' + ), + ] + errors: Annotated[ + list[error.Error] | None, Field(description='Errors for failed requests', min_length=1) + ] = None + response: Annotated[Response, Field(description='Preview data for successful requests')] + success: Annotated[Literal[True], Field(description='Indicates this preview request succeeded')] class Preview2(Preview1): @@ -115,7 +110,7 @@ class Preview2(Preview1): class Response1(AdCPBaseModel): - expires_at: AwareDatetime + expires_at: Annotated[AwareDatetime, Field(description='When the preview URLs expire')] interactive_url: AnyUrl | None = None previews: Annotated[ list[Preview2], @@ -124,11 +119,19 @@ class Response1(AdCPBaseModel): class Results1(AdCPBaseModel): - error: Annotated[Error, Field(description='Error information for failed requests')] + creative_id: Annotated[ + str, + Field( + description='ID of the creative this result corresponds to. Enables correlation when processing batch results.' + ), + ] + errors: Annotated[ + list[error.Error], Field(description='Errors for failed requests', min_length=1) + ] response: Annotated[ - Response1 | None, Field(description='Preview response for successful requests') + Response1 | None, Field(description='Preview data for successful requests') ] = None - success: Annotated[Literal[False], Field(description='Whether this preview request succeeded')] + success: Annotated[Literal[False], Field(description='Indicates this preview request failed')] class PreviewCreativeResponse2(AdCPBaseModel): diff --git a/src/adcp/types/generated_poc/enums/asset_content_type.py b/src/adcp/types/generated_poc/enums/asset_content_type.py index f3bc2ce5..77b443db 100644 --- a/src/adcp/types/generated_poc/enums/asset_content_type.py +++ b/src/adcp/types/generated_poc/enums/asset_content_type.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: enums/asset_content_type.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -20,3 +20,5 @@ class AssetContentType(Enum): daast = 'daast' url = 'url' webhook = 'webhook' + brief = 'brief' + catalog = 'catalog' diff --git a/src/adcp/types/generated_poc/enums/audience_source.py b/src/adcp/types/generated_poc/enums/audience_source.py new file mode 100644 index 00000000..a304e5c6 --- /dev/null +++ b/src/adcp/types/generated_poc/enums/audience_source.py @@ -0,0 +1,16 @@ +# generated by datamodel-codegen: +# filename: enums/audience_source.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum + + +class AudienceSource(Enum): + synced = 'synced' + platform = 'platform' + third_party = 'third_party' + lookalike = 'lookalike' + retargeting = 'retargeting' + unknown = 'unknown' diff --git a/src/adcp/types/generated_poc/enums/consent_basis.py b/src/adcp/types/generated_poc/enums/consent_basis.py new file mode 100644 index 00000000..3c262bc8 --- /dev/null +++ b/src/adcp/types/generated_poc/enums/consent_basis.py @@ -0,0 +1,14 @@ +# generated by datamodel-codegen: +# filename: enums/consent_basis.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum + + +class ConsentBasis(Enum): + consent = 'consent' + legitimate_interest = 'legitimate_interest' + contract = 'contract' + legal_obligation = 'legal_obligation' diff --git a/src/adcp/types/generated_poc/enums/device_type.py b/src/adcp/types/generated_poc/enums/device_type.py new file mode 100644 index 00000000..b7d45fb1 --- /dev/null +++ b/src/adcp/types/generated_poc/enums/device_type.py @@ -0,0 +1,16 @@ +# generated by datamodel-codegen: +# filename: enums/device_type.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum + + +class DeviceType(Enum): + desktop = 'desktop' + mobile = 'mobile' + tablet = 'tablet' + ctv = 'ctv' + dooh = 'dooh' + unknown = 'unknown' diff --git a/src/adcp/types/generated_poc/enums/digital_source_type.py b/src/adcp/types/generated_poc/enums/digital_source_type.py new file mode 100644 index 00000000..cbf90646 --- /dev/null +++ b/src/adcp/types/generated_poc/enums/digital_source_type.py @@ -0,0 +1,19 @@ +# generated by datamodel-codegen: +# filename: enums/digital_source_type.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum + + +class DigitalSourceType(Enum): + digital_capture = 'digital_capture' + digital_creation = 'digital_creation' + trained_algorithmic_media = 'trained_algorithmic_media' + composite_with_trained_algorithmic_media = 'composite_with_trained_algorithmic_media' + algorithmic_media = 'algorithmic_media' + composite_capture = 'composite_capture' + composite_synthetic = 'composite_synthetic' + human_edits = 'human_edits' + data_driven_media = 'data_driven_media' diff --git a/src/adcp/types/generated_poc/enums/disclosure_position.py b/src/adcp/types/generated_poc/enums/disclosure_position.py new file mode 100644 index 00000000..d1183ae3 --- /dev/null +++ b/src/adcp/types/generated_poc/enums/disclosure_position.py @@ -0,0 +1,18 @@ +# generated by datamodel-codegen: +# filename: enums/disclosure_position.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum + + +class DisclosurePosition(Enum): + prominent = 'prominent' + footer = 'footer' + audio = 'audio' + subtitle = 'subtitle' + overlay = 'overlay' + end_card = 'end_card' + pre_roll = 'pre_roll' + companion = 'companion' diff --git a/src/adcp/types/generated_poc/enums/error_code.py b/src/adcp/types/generated_poc/enums/error_code.py new file mode 100644 index 00000000..1836779e --- /dev/null +++ b/src/adcp/types/generated_poc/enums/error_code.py @@ -0,0 +1,30 @@ +# generated by datamodel-codegen: +# filename: enums/error_code.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum + + +class ErrorCode(Enum): + INVALID_REQUEST = 'INVALID_REQUEST' + AUTH_REQUIRED = 'AUTH_REQUIRED' + RATE_LIMITED = 'RATE_LIMITED' + SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE' + POLICY_VIOLATION = 'POLICY_VIOLATION' + PRODUCT_NOT_FOUND = 'PRODUCT_NOT_FOUND' + PRODUCT_UNAVAILABLE = 'PRODUCT_UNAVAILABLE' + PROPOSAL_EXPIRED = 'PROPOSAL_EXPIRED' + BUDGET_TOO_LOW = 'BUDGET_TOO_LOW' + CREATIVE_REJECTED = 'CREATIVE_REJECTED' + UNSUPPORTED_FEATURE = 'UNSUPPORTED_FEATURE' + AUDIENCE_TOO_SMALL = 'AUDIENCE_TOO_SMALL' + ACCOUNT_NOT_FOUND = 'ACCOUNT_NOT_FOUND' + ACCOUNT_SETUP_REQUIRED = 'ACCOUNT_SETUP_REQUIRED' + ACCOUNT_AMBIGUOUS = 'ACCOUNT_AMBIGUOUS' + ACCOUNT_PAYMENT_REQUIRED = 'ACCOUNT_PAYMENT_REQUIRED' + ACCOUNT_SUSPENDED = 'ACCOUNT_SUSPENDED' + COMPLIANCE_UNSATISFIED = 'COMPLIANCE_UNSATISFIED' + BUDGET_EXHAUSTED = 'BUDGET_EXHAUSTED' + CONFLICT = 'CONFLICT' diff --git a/src/adcp/types/generated_poc/enums/forecastable_metric.py b/src/adcp/types/generated_poc/enums/forecastable_metric.py index a8c70edd..5676638d 100644 --- a/src/adcp/types/generated_poc/enums/forecastable_metric.py +++ b/src/adcp/types/generated_poc/enums/forecastable_metric.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: enums/forecastable_metric.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -17,3 +17,7 @@ class ForecastableMetric(Enum): views = 'views' completed_views = 'completed_views' grps = 'grps' + engagements = 'engagements' + follows = 'follows' + saves = 'saves' + profile_visits = 'profile_visits' diff --git a/src/adcp/types/generated_poc/enums/media_buy_status.py b/src/adcp/types/generated_poc/enums/media_buy_status.py index 2f46c06d..1ebfd03a 100644 --- a/src/adcp/types/generated_poc/enums/media_buy_status.py +++ b/src/adcp/types/generated_poc/enums/media_buy_status.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: enums/media_buy_status.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -12,3 +12,5 @@ class MediaBuyStatus(Enum): active = 'active' paused = 'paused' completed = 'completed' + rejected = 'rejected' + canceled = 'canceled' diff --git a/src/adcp/types/generated_poc/enums/postal_system.py b/src/adcp/types/generated_poc/enums/postal_system.py index 13a07bcf..28de09af 100644 --- a/src/adcp/types/generated_poc/enums/postal_system.py +++ b/src/adcp/types/generated_poc/enums/postal_system.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: enums/postal_system.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -17,3 +17,5 @@ class PostalCodeSystem(Enum): de_plz = 'de_plz' fr_code_postal = 'fr_code_postal' au_postcode = 'au_postcode' + ch_plz = 'ch_plz' + at_plz = 'at_plz' diff --git a/src/adcp/types/generated_poc/enums/sort_metric.py b/src/adcp/types/generated_poc/enums/sort_metric.py new file mode 100644 index 00000000..629e03b9 --- /dev/null +++ b/src/adcp/types/generated_poc/enums/sort_metric.py @@ -0,0 +1,32 @@ +# generated by datamodel-codegen: +# filename: enums/sort_metric.json +# timestamp: 2026-02-28T17:39:50+00:00 + +from __future__ import annotations + +from enum import Enum + + +class SortMetric(Enum): + impressions = 'impressions' + spend = 'spend' + clicks = 'clicks' + ctr = 'ctr' + views = 'views' + completed_views = 'completed_views' + completion_rate = 'completion_rate' + conversions = 'conversions' + conversion_value = 'conversion_value' + roas = 'roas' + cost_per_acquisition = 'cost_per_acquisition' + new_to_brand_rate = 'new_to_brand_rate' + leads = 'leads' + grps = 'grps' + reach = 'reach' + frequency = 'frequency' + engagements = 'engagements' + follows = 'follows' + saves = 'saves' + profile_visits = 'profile_visits' + engagement_rate = 'engagement_rate' + cost_per_click = 'cost_per_click' diff --git a/src/adcp/types/generated_poc/enums/task_type.py b/src/adcp/types/generated_poc/enums/task_type.py index e69d440d..6cb5562e 100644 --- a/src/adcp/types/generated_poc/enums/task_type.py +++ b/src/adcp/types/generated_poc/enums/task_type.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: enums/task_type.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -19,6 +19,7 @@ class TaskType(Enum): list_property_lists = 'list_property_lists' delete_property_list = 'delete_property_list' sync_accounts = 'sync_accounts' + get_account_financials = 'get_account_financials' get_creative_delivery = 'get_creative_delivery' sync_event_sources = 'sync_event_sources' sync_audiences = 'sync_audiences' diff --git a/src/adcp/types/generated_poc/enums/uid_type.py b/src/adcp/types/generated_poc/enums/uid_type.py index 2409e728..adf3e15e 100644 --- a/src/adcp/types/generated_poc/enums/uid_type.py +++ b/src/adcp/types/generated_poc/enums/uid_type.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: enums/uid_type.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -13,6 +13,5 @@ class UidType(Enum): uid2 = 'uid2' euid = 'euid' pairid = 'pairid' - external_id = 'external_id' maid = 'maid' other = 'other' diff --git a/src/adcp/types/generated_poc/media_buy/build_creative_request.py b/src/adcp/types/generated_poc/media_buy/build_creative_request.py index 4bcd80cf..1f2eb4d1 100644 --- a/src/adcp/types/generated_poc/media_buy/build_creative_request.py +++ b/src/adcp/types/generated_poc/media_buy/build_creative_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/build_creative_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -11,7 +11,6 @@ from ..core import brand_ref from ..core import context as context_1 -from ..core import creative_brief_ref from ..core import creative_manifest as creative_manifest_1 from ..core import ext as ext_1 from ..core import format_id @@ -28,12 +27,6 @@ class BuildCreativeRequest(AdCPBaseModel): ), ] = None context: context_1.ContextObject | None = None - creative_brief: Annotated[ - creative_brief_ref.CreativeBriefReference | None, - Field( - description='Campaign-level creative brief with objective, audience, messaging, and reference assets. Can be an inline brief object or a URL to a hosted brief. Supplements the natural language message with structured creative direction.' - ), - ] = None creative_manifest: Annotated[ creative_manifest_1.CreativeManifest | None, Field( @@ -44,7 +37,7 @@ class BuildCreativeRequest(AdCPBaseModel): message: Annotated[ str | None, Field( - description='Natural language instructions for the transformation or generation. For pure generation, this is the creative brief. For transformation, this provides guidance on how to adapt the creative.' + description='Natural language instructions for the transformation or generation. For pure generation, this is the creative brief. For transformation, this provides guidance on how to adapt the creative. For refinement, this describes the desired changes.' ), ] = None target_format_id: Annotated[ diff --git a/src/adcp/types/generated_poc/media_buy/create_media_buy_request.py b/src/adcp/types/generated_poc/media_buy/create_media_buy_request.py index 978dcf7d..e74465cf 100644 --- a/src/adcp/types/generated_poc/media_buy/create_media_buy_request.py +++ b/src/adcp/types/generated_poc/media_buy/create_media_buy_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/create_media_buy_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,9 +10,10 @@ from adcp.types.base import AdCPBaseModel from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field -from ..core import brand_ref +from ..core import account_ref, brand_ref from ..core import context as context_1 from ..core import ext as ext_1 +from ..core import push_notification_config as push_notification_config_1 from ..core import reporting_webhook as reporting_webhook_1 from ..core import start_timing from ..enums import auth_scheme @@ -100,12 +101,12 @@ class CreateMediaBuyRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_id: Annotated[ - str | None, + account: Annotated[ + account_ref.AccountReference, Field( - description="Account to bill for this media buy. Required when the agent has access to multiple accounts; when omitted, the seller uses the agent's sole account. The seller maps the agent's brand + operator to an account during sync_accounts; the agent passes that account_id here." + description='Account to bill for this media buy. Pass an account_id from list_accounts (explicit model), or use a natural key (brand, operator) if the seller supports implicit_from_sync resolution.' ), - ] = None + ] artifact_webhook: Annotated[ ArtifactWebhook | None, Field( @@ -124,7 +125,12 @@ class CreateMediaBuyRequest(AdCPBaseModel): description="Buyer's campaign reference label. Groups related discovery and buy operations under a single campaign for CRM and ad server correlation (e.g., 'NovaDrink_Meals_Q2')." ), ] = None - buyer_ref: Annotated[str, Field(description="Buyer's reference identifier for this media buy")] + buyer_ref: Annotated[ + str, + Field( + description="Buyer's reference identifier for this media buy. Also serves as an idempotency key: sellers SHOULD deduplicate requests with the same buyer_ref and account, returning the existing media buy rather than creating a duplicate." + ), + ] context: context_1.ContextObject | None = None end_time: Annotated[ AwareDatetime, Field(description='Campaign end date/time in ISO 8601 format') @@ -144,6 +150,12 @@ class CreateMediaBuyRequest(AdCPBaseModel): description="ID of a proposal from get_products to execute. When provided with total_budget, the publisher converts the proposal's allocation percentages into packages automatically. Alternative to providing packages array." ), ] = None + push_notification_config: Annotated[ + push_notification_config_1.PushNotificationConfig | None, + Field( + description='Optional webhook configuration for async task status notifications. Publisher will send webhooks when status changes (working, input-required, completed, failed). The client generates an operation_id and embeds it in the URL before sending — the publisher echoes it back in webhook payloads for correlation.' + ), + ] = None reporting_webhook: Annotated[ reporting_webhook_1.ReportingWebhook | None, Field(description='Optional webhook configuration for automated reporting delivery'), diff --git a/src/adcp/types/generated_poc/media_buy/get_media_buy_delivery_request.py b/src/adcp/types/generated_poc/media_buy/get_media_buy_delivery_request.py index fd5fb8ab..f9ae3c51 100644 --- a/src/adcp/types/generated_poc/media_buy/get_media_buy_delivery_request.py +++ b/src/adcp/types/generated_poc/media_buy/get_media_buy_delivery_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/get_media_buy_delivery_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,9 +9,124 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field, RootModel +from ..core import account_ref from ..core import context as context_1 +from ..core import duration from ..core import ext as ext_1 -from ..enums import media_buy_status +from ..enums import attribution_model +from ..enums import geo_level as geo_level_1 +from ..enums import media_buy_status, metro_system, postal_system, sort_metric + + +class AttributionWindow(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + model: Annotated[ + attribution_model.AttributionModel | None, + Field( + description='Attribution model to use. When omitted, the seller applies their default model.' + ), + ] = None + post_click: Annotated[ + duration.Duration | None, Field(description='Post-click attribution window to apply.') + ] = None + post_view: Annotated[ + duration.Duration | None, Field(description='Post-view attribution window to apply.') + ] = None + + +class Audience(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + limit: Annotated[ + int | None, Field(description='Maximum number of entries to return. Defaults to 25.', ge=1) + ] = 25 + sort_by: Annotated[ + sort_metric.SortMetric | None, + Field( + description="Metric to sort breakdown rows by (descending). Falls back to 'spend' if the seller does not report the requested metric." + ), + ] = sort_metric.SortMetric.spend + + +class DevicePlatform(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + limit: Annotated[ + int | None, + Field( + description='Maximum number of entries to return. When omitted, all entries are returned (the enum is small and bounded).', + ge=1, + ), + ] = None + sort_by: Annotated[ + sort_metric.SortMetric | None, + Field( + description="Metric to sort breakdown rows by (descending). Falls back to 'spend' if the seller does not report the requested metric." + ), + ] = sort_metric.SortMetric.spend + + +class DeviceType(DevicePlatform): + pass + + +class Geo(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + geo_level: Annotated[ + geo_level_1.GeographicTargetingLevel, + Field(description='Geographic granularity level for the breakdown'), + ] + limit: Annotated[ + int | None, + Field( + description='Maximum number of geo entries to return. Defaults to 25. When truncated, by_geo_truncated is true in the response.', + ge=1, + ), + ] = 25 + sort_by: Annotated[ + sort_metric.SortMetric | None, + Field( + description="Metric to sort breakdown rows by (descending). Falls back to 'spend' if the seller does not report the requested metric." + ), + ] = sort_metric.SortMetric.spend + system: Annotated[ + metro_system.MetroAreaSystem | postal_system.PostalCodeSystem | None, + Field( + description="Classification system for metro or postal_area levels (e.g., 'nielsen_dma', 'us_zip'). Required when geo_level is 'metro' or 'postal_area'." + ), + ] = None + + +class Placement(Audience): + pass + + +class ReportingDimensions(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + audience: Annotated[ + Audience | None, Field(description='Request audience segment breakdown.') + ] = None + device_platform: Annotated[ + DevicePlatform | None, Field(description='Request device platform breakdown.') + ] = None + device_type: Annotated[ + DeviceType | None, Field(description='Request device type breakdown.') + ] = None + geo: Annotated[ + Geo | None, + Field( + description='Request geographic breakdown. Check reporting_capabilities.supports_geo_breakdown for available levels and systems.' + ), + ] = None + placement: Annotated[Placement | None, Field(description='Request placement breakdown.')] = None class StatusFilter(RootModel[list[media_buy_status.MediaBuyStatus]]): @@ -28,10 +143,16 @@ class GetMediaBuyDeliveryRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_id: Annotated[ - str | None, + account: Annotated[ + account_ref.AccountReference | None, Field( - description='Filter delivery data to a specific account. When provided, only returns media buys belonging to this account. When omitted, returns data across all accessible accounts. Optional if the agent has a single account.' + description='Filter delivery data to a specific account. When omitted, returns data across all accessible accounts.' + ), + ] = None + attribution_window: Annotated[ + AttributionWindow | None, + Field( + description='Attribution window to apply for conversion metrics. When provided, the seller returns conversion data using the requested lookback windows instead of their platform default. The seller echoes the applied window in the response. Sellers that do not support configurable windows ignore this field and return their default. Check get_adcp_capabilities conversion_tracking.attribution_windows for available options.' ), ] = None buyer_refs: Annotated[ @@ -47,12 +168,24 @@ class GetMediaBuyDeliveryRequest(AdCPBaseModel): ), ] = None ext: ext_1.ExtensionObject | None = None + include_package_daily_breakdown: Annotated[ + bool | None, + Field( + description='When true, include daily_breakdown arrays within each package in by_package. Useful for per-package pacing analysis and line-item monitoring. Omit or set false to reduce response size — package daily data can be large for multi-package buys over long flights.' + ), + ] = False media_buy_ids: Annotated[ list[str] | None, Field( description='Array of publisher media buy IDs to get delivery data for', min_length=1 ), ] = None + reporting_dimensions: Annotated[ + ReportingDimensions | None, + Field( + description='Request dimensional breakdowns in delivery reporting. Each key enables a specific breakdown dimension within by_package — include as an empty object (e.g., "device_type": {}) to activate with defaults. Omit entirely for no breakdowns (backward compatible). Unsupported dimensions are silently omitted from the response. Note: keyword, catalog_item, and creative breakdowns are returned automatically when the seller supports them and are not controlled by this object.' + ), + ] = None start_date: Annotated[ str | None, Field( diff --git a/src/adcp/types/generated_poc/media_buy/get_media_buy_delivery_response.py b/src/adcp/types/generated_poc/media_buy/get_media_buy_delivery_response.py index 6712381d..f9c65891 100644 --- a/src/adcp/types/generated_poc/media_buy/get_media_buy_delivery_response.py +++ b/src/adcp/types/generated_poc/media_buy/get_media_buy_delivery_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/get_media_buy_delivery_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -15,7 +15,11 @@ from ..core import error from ..core import ext as ext_1 from ..core.delivery_metrics import DeliveryMetrics +from ..enums import audience_source as audience_source_1 from ..enums import content_id_type as content_id_type_1 +from ..enums import device_platform as device_platform_1 +from ..enums import device_type as device_type_1 +from ..enums import geo_level as geo_level_1 from ..enums import pricing_model as pricing_model_1 @@ -70,6 +74,39 @@ class AggregatedTotals(AdCPBaseModel): ] = None +class MatchType(Enum): + broad = 'broad' + phrase = 'phrase' + exact = 'exact' + + +class DailyBreakdownItem(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + conversion_value: Annotated[ + float | None, Field(description='Daily conversion value for this package', ge=0.0) + ] = None + conversions: Annotated[ + float | None, Field(description='Daily conversions for this package', ge=0.0) + ] = None + date: Annotated[str, Field(description='Date (YYYY-MM-DD)', pattern='^\\d{4}-\\d{2}-\\d{2}$')] + impressions: Annotated[float, Field(description='Daily impressions for this package', ge=0.0)] + new_to_brand_rate: Annotated[ + float | None, + Field( + description='Daily fraction of conversions from first-time brand buyers (0 = none, 1 = all)', + ge=0.0, + le=1.0, + ), + ] = None + roas: Annotated[ + float | None, + Field(description='Daily return on ad spend (conversion_value / spend)', ge=0.0), + ] = None + spend: Annotated[float, Field(description='Daily spend for this package', ge=0.0)] + + class DeliveryStatus(Enum): delivering = 'delivering' completed = 'completed' @@ -78,7 +115,7 @@ class DeliveryStatus(Enum): goal_met = 'goal_met' -class DailyBreakdownItem(AdCPBaseModel): +class DailyBreakdownItem1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) @@ -109,6 +146,8 @@ class Status(Enum): active = 'active' paused = 'paused' completed = 'completed' + rejected = 'rejected' + canceled = 'canceled' failed = 'failed' reporting_delayed = 'reporting_delayed' @@ -134,6 +173,26 @@ class ReportingPeriod(AdCPBaseModel): ] +class ByAudienceItem(DeliveryMetrics): + audience_id: Annotated[ + str, + Field( + description="Audience segment identifier. For 'synced' source, matches audience_id from sync_audiences. For other sources, seller-defined." + ), + ] + audience_name: Annotated[ + str | None, Field(description='Human-readable audience segment name') + ] = None + audience_source: Annotated[ + audience_source_1.AudienceSource, + Field( + description='Origin of the audience segment (synced, platform, third_party, lookalike, retargeting, unknown)' + ), + ] + impressions: Any + spend: Any + + class ByCatalogItemItem(DeliveryMetrics): content_id: Annotated[ str, Field(description='Catalog item identifier (e.g., SKU, GTIN, job_id, offering_id)') @@ -162,10 +221,82 @@ class ByCreativeItem(DeliveryMetrics): spend: Any +class ByDevicePlatformItem(DeliveryMetrics): + device_platform: Annotated[ + device_platform_1.DevicePlatform, Field(description='Operating system platform') + ] + impressions: Any + spend: Any + + +class ByDeviceTypeItem(DeliveryMetrics): + device_type: Annotated[ + device_type_1.DeviceType, + Field(description='Device form factor (desktop, mobile, tablet, ctv, dooh, unknown)'), + ] + impressions: Any + spend: Any + + +class ByGeoItem(DeliveryMetrics): + geo_code: Annotated[ + str, + Field( + description="Geographic code within the level and system. Country: ISO 3166-1 alpha-2 ('US'). Region: ISO 3166-2 with country prefix ('US-CA'). Metro/postal: system-specific code ('501', '10001')." + ), + ] + geo_level: Annotated[ + geo_level_1.GeographicTargetingLevel, + Field(description='Geographic level of this entry (country, region, metro, postal_area)'), + ] + geo_name: Annotated[ + str | None, + Field( + description="Human-readable geographic name (e.g., 'United States', 'California', 'New York DMA')" + ), + ] = None + system: Annotated[ + str | None, + Field( + description="Classification system for metro or postal_area levels (e.g., 'nielsen_dma', 'us_zip'). Present when geo_level is 'metro' or 'postal_area'." + ), + ] = None + impressions: Any + spend: Any + + +class ByKeywordItem(DeliveryMetrics): + keyword: Annotated[str, Field(description='The targeted keyword')] + match_type: Annotated[MatchType, Field(description='Match type for this keyword')] + impressions: Any + spend: Any + + +class ByPlacementItem(DeliveryMetrics): + placement_id: Annotated[ + str, Field(description="Placement identifier from the product's placements array") + ] + placement_name: Annotated[str | None, Field(description='Human-readable placement name')] = None + impressions: Any + spend: Any + + class ByPackageItem(DeliveryMetrics): buyer_ref: Annotated[ str | None, Field(description="Buyer's reference identifier for this package") ] = None + by_audience: Annotated[ + list[ByAudienceItem] | None, + Field( + description="Delivery by audience segment within this package. Available when the buyer requests audience breakdown via reporting_dimensions and the seller supports it. Only 'synced' audiences are directly targetable via the targeting overlay; other sources are informational." + ), + ] = None + by_audience_truncated: Annotated[ + bool | None, + Field( + description='Whether by_audience was truncated. Sellers MUST return this flag whenever by_audience is present (false means the list is complete).' + ), + ] = None by_catalog_item: Annotated[ list[ByCatalogItemItem] | None, Field( @@ -178,6 +309,60 @@ class ByPackageItem(DeliveryMetrics): description='Metrics broken down by creative within this package. Available when the seller supports creative-level reporting.' ), ] = None + by_device_platform: Annotated[ + list[ByDevicePlatformItem] | None, + Field( + description='Delivery by operating system within this package. Available when the buyer requests device_platform breakdown via reporting_dimensions and the seller supports it. Useful for CTV campaigns where tvOS vs Roku OS vs Fire OS matters.' + ), + ] = None + by_device_platform_truncated: Annotated[ + bool | None, + Field( + description='Whether by_device_platform was truncated. Sellers MUST return this flag whenever by_device_platform is present (false means the list is complete).' + ), + ] = None + by_device_type: Annotated[ + list[ByDeviceTypeItem] | None, + Field( + description='Delivery by device form factor within this package. Available when the buyer requests device_type breakdown via reporting_dimensions and the seller supports it.' + ), + ] = None + by_device_type_truncated: Annotated[ + bool | None, + Field( + description='Whether by_device_type was truncated. Sellers MUST return this flag whenever by_device_type is present (false means the list is complete).' + ), + ] = None + by_geo: Annotated[ + list[ByGeoItem] | None, + Field( + description="Delivery by geographic area within this package. Available when the buyer requests geo breakdown via reporting_dimensions and the seller supports it. Each dimension's rows are independent slices that should sum to the package total." + ), + ] = None + by_geo_truncated: Annotated[ + bool | None, + Field( + description='Whether by_geo was truncated due to the requested limit or a seller-imposed maximum. Sellers MUST return this flag whenever by_geo is present (false means the list is complete).' + ), + ] = None + by_keyword: Annotated[ + list[ByKeywordItem] | None, + Field( + description='Metrics broken down by keyword within this package. One row per (keyword, match_type) pair — the same keyword with different match types appears as separate rows. Keyword-grain only: rows reflect aggregate performance of each targeted keyword, not individual search queries. Rows may not sum to package totals when a single impression is attributed to the triggering keyword only. Available for search and retail media packages when the seller supports keyword-level reporting.' + ), + ] = None + by_placement: Annotated[ + list[ByPlacementItem] | None, + Field( + description="Delivery by placement within this package. Available when the buyer requests placement breakdown via reporting_dimensions and the seller supports it. Placement IDs reference the product's placements array." + ), + ] = None + by_placement_truncated: Annotated[ + bool | None, + Field( + description='Whether by_placement was truncated. Sellers MUST return this flag whenever by_placement is present (false means the list is complete).' + ), + ] = None currency: Annotated[ str, Field( @@ -185,6 +370,12 @@ class ByPackageItem(DeliveryMetrics): pattern='^[A-Z]{3}$', ), ] + daily_breakdown: Annotated[ + list[DailyBreakdownItem] | None, + Field( + description='Day-by-day delivery for this package. Only present when include_package_daily_breakdown is true in the request. Enables per-package pacing analysis and line-item monitoring.' + ), + ] = None delivery_status: Annotated[ DeliveryStatus | None, Field( @@ -241,7 +432,7 @@ class MediaBuyDelivery(AdCPBaseModel): ] = None by_package: Annotated[list[ByPackageItem], Field(description='Metrics broken down by package')] daily_breakdown: Annotated[ - list[DailyBreakdownItem] | None, Field(description='Day-by-day delivery') + list[DailyBreakdownItem1] | None, Field(description='Day-by-day delivery') ] = None expected_availability: Annotated[ AwareDatetime | None, @@ -263,7 +454,7 @@ class MediaBuyDelivery(AdCPBaseModel): status: Annotated[ Status, Field( - description='Current media buy status. Lifecycle states use the same taxonomy as media-buy-status (`pending_activation`, `active`, `paused`, `completed`). In webhook context, reporting_delayed indicates data temporarily unavailable. `pending` is accepted as a legacy alias for pending_activation.' + description='Current media buy status. Lifecycle states use the same taxonomy as media-buy-status (`pending_activation`, `active`, `paused`, `completed`, `rejected`, `canceled`). In webhook context, reporting_delayed indicates data temporarily unavailable. `pending` is accepted as a legacy alias for pending_activation.' ), ] totals: Totals diff --git a/src/adcp/types/generated_poc/media_buy/get_media_buys_request.py b/src/adcp/types/generated_poc/media_buy/get_media_buys_request.py index e2f5893d..cad49a59 100644 --- a/src/adcp/types/generated_poc/media_buy/get_media_buys_request.py +++ b/src/adcp/types/generated_poc/media_buy/get_media_buys_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/get_media_buys_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -9,6 +9,7 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field, RootModel +from ..core import account_ref from ..core import context as context_1 from ..core import ext as ext_1 from ..core import pagination_request @@ -29,12 +30,9 @@ class GetMediaBuysRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_id: Annotated[ - str | None, - Field( - description='Filter to a specific account. When omitted, returns media buys across all accessible accounts. Optional if the agent has a single account.' - ), - ] = None + account: Annotated[ + account_ref.AccountReference, Field(description='Account to retrieve media buys for.') + ] buyer_refs: Annotated[ list[str] | None, Field(description='Array of buyer reference IDs to retrieve', min_length=1), diff --git a/src/adcp/types/generated_poc/media_buy/get_media_buys_response.py b/src/adcp/types/generated_poc/media_buy/get_media_buys_response.py index 1d986f04..cb1867a8 100644 --- a/src/adcp/types/generated_poc/media_buy/get_media_buys_response.py +++ b/src/adcp/types/generated_poc/media_buy/get_media_buys_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/get_media_buys_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -203,6 +203,12 @@ class MediaBuy(AdCPBaseModel): pattern='^[A-Z]{3}$', ), ] + end_time: Annotated[ + AwareDatetime | None, + Field( + description='ISO 8601 flight end time for this media buy (latest package end_time). Avoids requiring buyers to compute max(packages[].end_time).' + ), + ] = None ext: ext_1.ExtensionObject | None = None media_buy_id: Annotated[ str, Field(description="Publisher's unique identifier for the media buy") @@ -213,6 +219,12 @@ class MediaBuy(AdCPBaseModel): description='Packages within this media buy, augmented with creative approval status and optional delivery snapshots' ), ] + start_time: Annotated[ + AwareDatetime | None, + Field( + description='ISO 8601 flight start time for this media buy (earliest package start_time). Avoids requiring buyers to compute min(packages[].start_time).' + ), + ] = None status: media_buy_status.MediaBuyStatus total_budget: Annotated[ float, diff --git a/src/adcp/types/generated_poc/media_buy/get_products_request.py b/src/adcp/types/generated_poc/media_buy/get_products_request.py index 21f16747..e6f3b6f8 100644 --- a/src/adcp/types/generated_poc/media_buy/get_products_request.py +++ b/src/adcp/types/generated_poc/media_buy/get_products_request.py @@ -1,29 +1,141 @@ # generated by datamodel-codegen: # filename: media_buy/get_products_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations -from typing import Annotated +from enum import Enum +from typing import Annotated, Any, Literal from adcp.types.base import AdCPBaseModel -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, RootModel -from ..core import brand_ref +from ..core import account_ref, brand_ref from ..core import catalog as catalog_1 from ..core import context as context_1 from ..core import ext as ext_1 from ..core import pagination_request, product_filters, property_list_ref -class GetProductsRequest(AdCPBaseModel): +class BuyingMode(Enum): + brief = 'brief' + wholesale = 'wholesale' + refine = 'refine' + + +class FieldModel(Enum): + product_id = 'product_id' + name = 'name' + description = 'description' + publisher_properties = 'publisher_properties' + channels = 'channels' + format_ids = 'format_ids' + placements = 'placements' + delivery_type = 'delivery_type' + pricing_options = 'pricing_options' + forecast = 'forecast' + outcome_measurement = 'outcome_measurement' + delivery_measurement = 'delivery_measurement' + reporting_capabilities = 'reporting_capabilities' + creative_policy = 'creative_policy' + catalog_types = 'catalog_types' + metric_optimization = 'metric_optimization' + conversion_tracking = 'conversion_tracking' + data_provider_signals = 'data_provider_signals' + max_optimization_goals = 'max_optimization_goals' + catalog_match = 'catalog_match' + brief_relevance = 'brief_relevance' + expires_at = 'expires_at' + product_card = 'product_card' + product_card_detailed = 'product_card_detailed' + + +class Refine(AdCPBaseModel): model_config = ConfigDict( - extra='allow', + extra='forbid', ) - account_id: Annotated[ + ask: Annotated[ + str, + Field( + description="What the buyer is asking for at the request level (e.g., 'more video options and less display', 'suggest how to combine these products').", + min_length=1, + ), + ] + scope: Annotated[ + Literal['request'], + Field( + description='Change scoped to the overall request — direction for the selection as a whole.' + ), + ] + + +class Action(Enum): + include = 'include' + omit = 'omit' + more_like_this = 'more_like_this' + + +class Refine1(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + action: Annotated[ + Action, + Field( + description="'include': return this product with updated pricing and data. 'omit': exclude this product from the response. 'more_like_this': find additional products similar to this one (the original is also returned)." + ), + ] + ask: Annotated[ str | None, Field( - description="Account ID for product lookup. Required when the seller declares account.required_for_products = true in capabilities. Returns products with pricing specific to this account's rate card." + description="What the buyer is asking for on this product. For 'include': specific changes to request (e.g., 'add 16:9 format'). For 'more_like_this': what 'similar' means (e.g., 'same audience but video format'). Ignored when action is 'omit'.", + min_length=1, + ), + ] = None + id: Annotated[ + str, Field(description='Product ID from a previous get_products response.', min_length=1) + ] + scope: Annotated[Literal['product'], Field(description='Change scoped to a specific product.')] + + +class Action2(Enum): + include = 'include' + omit = 'omit' + + +class Refine2(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + action: Annotated[ + Action2, + Field( + description="'include': return this proposal with updated allocations and pricing. 'omit': exclude this proposal from the response." + ), + ] + ask: Annotated[ + str | None, + Field( + description="What the buyer is asking for on this proposal (e.g., 'shift more budget toward video', 'reduce total by 10%'). Ignored when action is 'omit'.", + min_length=1, + ), + ] = None + id: Annotated[ + str, Field(description='Proposal ID from a previous get_products response.', min_length=1) + ] + scope: Annotated[ + Literal['proposal'], Field(description='Change scoped to a specific proposal.') + ] + + +class GetProductsRequest1(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + account: Annotated[ + account_ref.AccountReference | None, + Field( + description="Account for product lookup. Returns products with pricing specific to this account's rate card." ), ] = None brand: Annotated[ @@ -33,14 +145,78 @@ class GetProductsRequest(AdCPBaseModel): ), ] = None brief: Annotated[ - str | None, Field(description='Natural language description of campaign requirements.') + str, + Field( + description="Natural language description of campaign requirements. Required when buying_mode is 'brief'. Must not be provided when buying_mode is 'wholesale' or 'refine'." + ), + ] + buyer_campaign_ref: Annotated[ + str | None, + Field( + description="Buyer's campaign reference label. Groups related discovery and buy operations under a single campaign for CRM and ad server correlation (e.g., 'NovaDrink_Meals_Q2')." + ), ] = None + buying_mode: Annotated[ + Literal['brief'], + Field( + description="Declares buyer intent for this request. 'brief': publisher curates product recommendations from the provided brief. 'wholesale': buyer requests raw inventory to apply their own audiences — brief must not be provided, and proposals are omitted. 'refine': iterate on products and proposals from a previous get_products response using the refine array of change requests." + ), + ] + catalog: Annotated[ + catalog_1.Catalog | None, + Field( + description='Catalog of items the buyer wants to promote. The seller matches catalog items against its inventory and returns products where matches exist. Supports all catalog types: a job catalog finds job ad products, a product catalog finds sponsored product slots. Reference a synced catalog by catalog_id, or provide inline items.' + ), + ] = None + context: context_1.ContextObject | None = None + ext: ext_1.ExtensionObject | None = None + fields: Annotated[ + list[FieldModel] | None, + Field( + description='Specific product fields to include in the response. When omitted, all fields are returned. Use for lightweight discovery calls where only a subset of product data is needed (e.g., just IDs and pricing for comparison). Required fields (product_id, name) are always included regardless of selection.', + min_length=1, + ), + ] = None + filters: product_filters.ProductFilters | None = None + pagination: pagination_request.PaginationRequest | None = None + property_list: Annotated[ + property_list_ref.PropertyListReference | None, + Field( + description='[AdCP 3.0] Reference to an externally managed property list. When provided, the sales agent should filter products to only those available on properties in the list.' + ), + ] = None + refine: Any | None = None + + +class GetProductsRequest2(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + account: Annotated[ + account_ref.AccountReference | None, + Field( + description="Account for product lookup. Returns products with pricing specific to this account's rate card." + ), + ] = None + brand: Annotated[ + brand_ref.BrandReference | None, + Field( + description='Brand reference for product discovery context. Resolved to full brand identity at execution time.' + ), + ] = None + brief: Any | None = None buyer_campaign_ref: Annotated[ str | None, Field( description="Buyer's campaign reference label. Groups related discovery and buy operations under a single campaign for CRM and ad server correlation (e.g., 'NovaDrink_Meals_Q2')." ), ] = None + buying_mode: Annotated[ + Literal['wholesale'], + Field( + description="Declares buyer intent for this request. 'brief': publisher curates product recommendations from the provided brief. 'wholesale': buyer requests raw inventory to apply their own audiences — brief must not be provided, and proposals are omitted. 'refine': iterate on products and proposals from a previous get_products response using the refine array of change requests." + ), + ] catalog: Annotated[ catalog_1.Catalog | None, Field( @@ -49,6 +225,13 @@ class GetProductsRequest(AdCPBaseModel): ] = None context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None + fields: Annotated[ + list[FieldModel] | None, + Field( + description='Specific product fields to include in the response. When omitted, all fields are returned. Use for lightweight discovery calls where only a subset of product data is needed (e.g., just IDs and pricing for comparison). Required fields (product_id, name) are always included regardless of selection.', + min_length=1, + ), + ] = None filters: product_filters.ProductFilters | None = None pagination: pagination_request.PaginationRequest | None = None property_list: Annotated[ @@ -57,3 +240,77 @@ class GetProductsRequest(AdCPBaseModel): description='[AdCP 3.0] Reference to an externally managed property list. When provided, the sales agent should filter products to only those available on properties in the list.' ), ] = None + refine: Any | None = None + + +class GetProductsRequest3(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + account: Annotated[ + account_ref.AccountReference | None, + Field( + description="Account for product lookup. Returns products with pricing specific to this account's rate card." + ), + ] = None + brand: Annotated[ + brand_ref.BrandReference | None, + Field( + description='Brand reference for product discovery context. Resolved to full brand identity at execution time.' + ), + ] = None + brief: Any | None = None + buyer_campaign_ref: Annotated[ + str | None, + Field( + description="Buyer's campaign reference label. Groups related discovery and buy operations under a single campaign for CRM and ad server correlation (e.g., 'NovaDrink_Meals_Q2')." + ), + ] = None + buying_mode: Annotated[ + Literal['refine'], + Field( + description="Declares buyer intent for this request. 'brief': publisher curates product recommendations from the provided brief. 'wholesale': buyer requests raw inventory to apply their own audiences — brief must not be provided, and proposals are omitted. 'refine': iterate on products and proposals from a previous get_products response using the refine array of change requests." + ), + ] + catalog: Annotated[ + catalog_1.Catalog | None, + Field( + description='Catalog of items the buyer wants to promote. The seller matches catalog items against its inventory and returns products where matches exist. Supports all catalog types: a job catalog finds job ad products, a product catalog finds sponsored product slots. Reference a synced catalog by catalog_id, or provide inline items.' + ), + ] = None + context: context_1.ContextObject | None = None + ext: ext_1.ExtensionObject | None = None + fields: Annotated[ + list[FieldModel] | None, + Field( + description='Specific product fields to include in the response. When omitted, all fields are returned. Use for lightweight discovery calls where only a subset of product data is needed (e.g., just IDs and pricing for comparison). Required fields (product_id, name) are always included regardless of selection.', + min_length=1, + ), + ] = None + filters: product_filters.ProductFilters | None = None + pagination: pagination_request.PaginationRequest | None = None + property_list: Annotated[ + property_list_ref.PropertyListReference | None, + Field( + description='[AdCP 3.0] Reference to an externally managed property list. When provided, the sales agent should filter products to only those available on properties in the list.' + ), + ] = None + refine: Annotated[ + list[Refine | Refine1 | Refine2], + Field( + description="Array of change requests for iterating on products and proposals from a previous get_products response. Each entry declares a scope (request, product, or proposal) and what the buyer is asking for. Only valid when buying_mode is 'refine'. The seller responds to each entry via refinement_applied in the response, matched by position.", + min_length=1, + ), + ] + + +class GetProductsRequest( + RootModel[GetProductsRequest1 | GetProductsRequest2 | GetProductsRequest3] +): + root: Annotated[ + GetProductsRequest1 | GetProductsRequest2 | GetProductsRequest3, + Field( + description="Request parameters for discovering or refining advertising products. buying_mode declares the buyer's intent: 'brief' for curated discovery, 'wholesale' for raw catalog access, or 'refine' to iterate on known products and proposals.", + title='Get Products Request', + ), + ] diff --git a/src/adcp/types/generated_poc/media_buy/get_products_response.py b/src/adcp/types/generated_poc/media_buy/get_products_response.py index 17ff110f..310c42bc 100644 --- a/src/adcp/types/generated_poc/media_buy/get_products_response.py +++ b/src/adcp/types/generated_poc/media_buy/get_products_response.py @@ -1,9 +1,10 @@ # generated by datamodel-codegen: # filename: media_buy/get_products_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations +from enum import Enum from typing import Annotated from adcp.types.base import AdCPBaseModel @@ -12,7 +13,51 @@ from ..core import context as context_1 from ..core import error from ..core import ext as ext_1 -from ..core import pagination_response, product, proposal +from ..core import pagination_response +from ..core import product as product_1 +from ..core import proposal as proposal_1 + + +class Scope(Enum): + request = 'request' + product = 'product' + proposal = 'proposal' + + +class Status(Enum): + applied = 'applied' + partial = 'partial' + unable = 'unable' + + +class RefinementAppliedItem(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + id: Annotated[ + str | None, + Field( + description='Echoes the id from the corresponding refine entry (for product and proposal scopes).' + ), + ] = None + notes: Annotated[ + str | None, + Field( + description="Seller explanation of what was done, what couldn't be done, or why. Recommended when status is 'partial' or 'unable'." + ), + ] = None + scope: Annotated[ + Scope | None, + Field( + description='Echoes the scope from the corresponding refine entry. Allows orchestrators to cross-validate alignment.' + ), + ] = None + status: Annotated[ + Status, + Field( + description="'applied': the ask was fulfilled. 'partial': the ask was partially fulfilled — see notes for details. 'unable': the seller could not fulfill the ask — see notes for why." + ), + ] class GetProductsResponse(AdCPBaseModel): @@ -32,7 +77,7 @@ class GetProductsResponse(AdCPBaseModel): ] = None ext: ext_1.ExtensionObject | None = None pagination: pagination_response.PaginationResponse | None = None - products: Annotated[list[product.Product], Field(description='Array of matching products')] + products: Annotated[list[product_1.Product], Field(description='Array of matching products')] property_list_applied: Annotated[ bool | None, Field( @@ -40,11 +85,17 @@ class GetProductsResponse(AdCPBaseModel): ), ] = None proposals: Annotated[ - list[proposal.Proposal] | None, + list[proposal_1.Proposal] | None, Field( description='Optional array of proposed media plans with budget allocations across products. Publishers include proposals when they can provide strategic guidance based on the brief. Proposals are actionable - buyers can refine them via follow-up get_products calls within the same session, or execute them directly via create_media_buy.' ), ] = None + refinement_applied: Annotated[ + list[RefinementAppliedItem] | None, + Field( + description="Seller's response to each change request in the refine array, matched by position. Each entry acknowledges whether the corresponding ask was applied, partially applied, or unable to be fulfilled. MUST contain the same number of entries in the same order as the request's refine array. Only present when the request used buying_mode: 'refine'." + ), + ] = None sandbox: Annotated[ bool | None, Field(description='When true, this response contains simulated data from sandbox mode.'), diff --git a/src/adcp/types/generated_poc/media_buy/list_creative_formats_request.py b/src/adcp/types/generated_poc/media_buy/list_creative_formats_request.py index 1bc2ea39..9dcbf61e 100644 --- a/src/adcp/types/generated_poc/media_buy/list_creative_formats_request.py +++ b/src/adcp/types/generated_poc/media_buy/list_creative_formats_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/list_creative_formats_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -12,7 +12,7 @@ from ..core import context as context_1 from ..core import ext as ext_1 from ..core import format_id, pagination_request -from ..enums import asset_content_type, format_category +from ..enums import asset_content_type, disclosure_position, format_category from ..enums import wcag_level as wcag_level_1 @@ -28,6 +28,13 @@ class ListCreativeFormatsRequest(AdCPBaseModel): ), ] = None context: context_1.ContextObject | None = None + disclosure_positions: Annotated[ + list[disclosure_position.DisclosurePosition] | None, + Field( + description="Filter to formats whose supported_disclosure_positions include all of these positions. Use to find formats compatible with a brief's compliance requirements.", + min_length=1, + ), + ] = None ext: ext_1.ExtensionObject | None = None format_ids: Annotated[ list[format_id.FormatId] | None, diff --git a/src/adcp/types/generated_poc/media_buy/list_creatives_request.py b/src/adcp/types/generated_poc/media_buy/list_creatives_request.py index 74d33760..fe8aa2b4 100644 --- a/src/adcp/types/generated_poc/media_buy/list_creatives_request.py +++ b/src/adcp/types/generated_poc/media_buy/list_creatives_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/list_creatives_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -17,7 +17,7 @@ from ..enums import creative_sort_field, sort_direction -class FieldModel(Enum): +class Field1(Enum): creative_id = 'creative_id' name = 'name' format = 'format' @@ -49,7 +49,7 @@ class ListCreativesRequest(AdCPBaseModel): context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None fields: Annotated[ - list[FieldModel] | None, + list[Field1] | None, Field( description='Specific fields to include in response (omit for all fields)', min_length=1 ), diff --git a/src/adcp/types/generated_poc/media_buy/list_creatives_response.py b/src/adcp/types/generated_poc/media_buy/list_creatives_response.py index 0a72f9b6..96d05488 100644 --- a/src/adcp/types/generated_poc/media_buy/list_creatives_response.py +++ b/src/adcp/types/generated_poc/media_buy/list_creatives_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/list_creatives_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -10,22 +10,25 @@ from pydantic import AwareDatetime, ConfigDict, Field, StringConstraints from ..core import account as account_1 -from ..core import catalog from ..core import context as context_1 from ..core import ext as ext_1 from ..core import format_id as format_id_1 from ..core import pagination_response, sub_asset from ..core.assets import ( audio_asset, + brief_asset, + catalog_asset, css_asset, daast_asset, html_asset, image_asset, javascript_asset, + markdown_asset, text_asset, url_asset, vast_asset, video_asset, + webhook_asset, ) from ..enums import creative_status, sort_direction @@ -126,29 +129,29 @@ class Creative(AdCPBaseModel): ] = None assets: Annotated[ dict[ - Annotated[str, StringConstraints(pattern=r'^[a-zA-Z0-9_-]+$')], + Annotated[str, StringConstraints(pattern=r'^[a-z0-9_]+$')], image_asset.ImageAsset | video_asset.VideoAsset | audio_asset.AudioAsset + | vast_asset.VastAsset | text_asset.TextAsset + | url_asset.UrlAsset | html_asset.HtmlAsset - | css_asset.CssAsset | javascript_asset.JavascriptAsset - | vast_asset.VastAsset + | webhook_asset.WebhookAsset + | css_asset.CssAsset | daast_asset.DaastAsset - | url_asset.UrlAsset, + | markdown_asset.MarkdownAsset + | brief_asset.BriefAsset + | catalog_asset.CatalogAsset, ] | None, - Field(description='Assets for this creative, keyed by asset_role'), + Field(description='Assets for this creative, keyed by asset_id'), ] = None assignments: Annotated[ Assignments | None, Field(description='Current package assignments (included when include_assignments=true)'), ] = None - catalogs: Annotated[ - list[catalog.Catalog] | None, - Field(description='Catalogs this creative renders, if any', min_length=1), - ] = None created_date: Annotated[ AwareDatetime, Field(description='When the creative was uploaded to the library') ] diff --git a/src/adcp/types/generated_poc/media_buy/package_request.py b/src/adcp/types/generated_poc/media_buy/package_request.py index c4b51106..e41724ae 100644 --- a/src/adcp/types/generated_poc/media_buy/package_request.py +++ b/src/adcp/types/generated_poc/media_buy/package_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/package_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,12 +9,9 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field -from ..core import catalog as catalog_1 -from ..core import creative_asset, creative_assignment +from ..core import catalog, creative_asset, creative_assignment from ..core import ext as ext_1 -from ..core import format_id -from ..core import optimization_goal as optimization_goal_1 -from ..core import targeting +from ..core import format_id, optimization_goal, targeting from ..enums import pacing as pacing_1 @@ -34,10 +31,10 @@ class PackageRequest(AdCPBaseModel): Field(description="Budget allocation for this package in the media buy's currency", ge=0.0), ] buyer_ref: Annotated[str, Field(description="Buyer's reference identifier for this package")] - catalog: Annotated[ - catalog_1.Catalog | None, + catalogs: Annotated[ + list[catalog.Catalog] | None, Field( - description='Catalog this package promotes. Makes the package catalog-driven: one budget envelope, platform optimizes across items. Reference a synced catalog by catalog_id with optional selectors to narrow scope.' + description='Catalogs this package promotes. Each catalog MUST have a distinct type (e.g., one product catalog, one store catalog). This constraint is enforced at the application level — sellers MUST reject requests containing multiple catalogs of the same type with a validation_error. Makes the package catalog-driven: one budget envelope, platform optimizes across items.' ), ] = None creative_assignments: Annotated[ @@ -66,7 +63,13 @@ class PackageRequest(AdCPBaseModel): impressions: Annotated[ float | None, Field(description='Impression goal for this package', ge=0.0) ] = None - optimization_goal: optimization_goal_1.OptimizationGoal | None = None + optimization_goals: Annotated[ + list[optimization_goal.OptimizationGoal] | None, + Field( + description='Optimization targets for this package. The seller optimizes delivery toward these goals in priority order. Common pattern: event goals (purchase, install) as primary targets at priority 1; metric goals (clicks, views) as secondary proxy signals at priority 2+.', + min_length=1, + ), + ] = None pacing: pacing_1.Pacing | None = None paused: Annotated[ bool | None, diff --git a/src/adcp/types/generated_poc/media_buy/package_update.py b/src/adcp/types/generated_poc/media_buy/package_update.py index 5c938fd4..63aef095 100644 --- a/src/adcp/types/generated_poc/media_buy/package_update.py +++ b/src/adcp/types/generated_poc/media_buy/package_update.py @@ -1,22 +1,82 @@ # generated by datamodel-codegen: # filename: media_buy/package_update.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations +from enum import Enum from typing import Annotated from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field, RootModel -from ..core import catalog as catalog_1 -from ..core import creative_asset, creative_assignment +from ..core import catalog, creative_asset, creative_assignment from ..core import ext as ext_1 -from ..core import optimization_goal as optimization_goal_1 -from ..core import targeting +from ..core import optimization_goal, targeting from ..enums import pacing as pacing_1 +class MatchType(Enum): + broad = 'broad' + phrase = 'phrase' + exact = 'exact' + + +class KeywordTargetsAddItem(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + bid_price: Annotated[ + float | None, + Field( + description="Per-keyword bid price. Inherits currency and max_bid interpretation from the package's pricing option.", + ge=0.0, + ), + ] = None + keyword: Annotated[str, Field(description='The keyword to target', min_length=1)] + match_type: Annotated[MatchType, Field(description='Match type for this keyword')] + + +class KeywordTargetsRemoveItem(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + keyword: Annotated[str, Field(description='The keyword to stop targeting', min_length=1)] + match_type: Annotated[MatchType, Field(description='Match type to remove')] + + +class NegativeKeywordsAddItem(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + keyword: Annotated[str, Field(description='The keyword to exclude', min_length=1)] + match_type: Annotated[MatchType, Field(description='Match type for exclusion')] + + +class NegativeKeywordsRemoveItem(AdCPBaseModel): + model_config = ConfigDict( + extra='forbid', + ) + keyword: Annotated[str, Field(description='The keyword to stop excluding', min_length=1)] + match_type: Annotated[MatchType, Field(description='Match type to remove')] + + +class KeywordTargetsAddItem1(KeywordTargetsAddItem): + pass + + +class KeywordTargetsRemoveItem1(KeywordTargetsRemoveItem): + pass + + +class NegativeKeywordsAddItem1(NegativeKeywordsAddItem): + pass + + +class NegativeKeywordsRemoveItem1(NegativeKeywordsRemoveItem): + pass + + class PackageUpdate1(AdCPBaseModel): model_config = ConfigDict( extra='allow', @@ -38,10 +98,11 @@ class PackageUpdate1(AdCPBaseModel): buyer_ref: Annotated[ str | None, Field(description="Buyer's reference for the package to update") ] = None - catalog: Annotated[ - catalog_1.Catalog | None, + catalogs: Annotated[ + list[catalog.Catalog] | None, Field( - description='Update the catalog this package promotes. Replaces the current catalog reference.' + description='Replace the catalogs this package promotes. Uses replacement semantics — the provided array replaces the current list. Omit to leave catalogs unchanged.', + min_length=1, ), ] = None creative_assignments: Annotated[ @@ -62,14 +123,53 @@ class PackageUpdate1(AdCPBaseModel): impressions: Annotated[ float | None, Field(description='Updated impression goal for this package', ge=0.0) ] = None - optimization_goal: optimization_goal_1.OptimizationGoal | None = None + keyword_targets_add: Annotated[ + list[KeywordTargetsAddItem] | None, + Field( + description='Keyword targets to add or update on this package. Upserts by (keyword, match_type) identity: if the pair already exists, its bid_price is updated; if not, a new keyword target is added. Use targeting_overlay.keyword_targets in create_media_buy to set the initial list.', + min_length=1, + ), + ] = None + keyword_targets_remove: Annotated[ + list[KeywordTargetsRemoveItem] | None, + Field( + description='Keyword targets to remove from this package. Removes matching (keyword, match_type) pairs. If a specified pair is not present, sellers SHOULD treat it as a no-op for that entry.', + min_length=1, + ), + ] = None + negative_keywords_add: Annotated[ + list[NegativeKeywordsAddItem] | None, + Field( + description='Negative keywords to add to this package. Appends to the existing negative keyword list — does not replace it. If a keyword+match_type pair already exists, sellers SHOULD treat it as a no-op for that entry. Use targeting_overlay.negative_keywords in create_media_buy to set the initial list.', + min_length=1, + ), + ] = None + negative_keywords_remove: Annotated[ + list[NegativeKeywordsRemoveItem] | None, + Field( + description='Negative keywords to remove from this package. Removes matching keyword+match_type pairs from the existing list. If a specified pair is not present, sellers SHOULD treat it as a no-op for that entry.', + min_length=1, + ), + ] = None + optimization_goals: Annotated[ + list[optimization_goal.OptimizationGoal] | None, + Field( + description='Replace all optimization goals for this package. Uses replacement semantics — omit to leave goals unchanged.', + min_length=1, + ), + ] = None pacing: pacing_1.Pacing | None = None package_id: Annotated[str, Field(description="Publisher's ID of package to update")] paused: Annotated[ bool | None, Field(description='Pause/resume specific package (true = paused, false = active)'), ] = None - targeting_overlay: targeting.TargetingOverlay | None = None + targeting_overlay: Annotated[ + targeting.TargetingOverlay | None, + Field( + description='Targeting overlay to apply to this package. Uses replacement semantics — the full overlay replaces the previous one. Omit to leave targeting unchanged. For keyword and negative keyword updates, prefer the incremental operations (keyword_targets_add, keyword_targets_remove, negative_keywords_add, negative_keywords_remove) which avoid replacing the full overlay. Sellers SHOULD return a validation error if targeting_overlay.keyword_targets is present in the same request as keyword_targets_add or keyword_targets_remove, and likewise for negative_keywords.' + ), + ] = None class PackageUpdate2(AdCPBaseModel): @@ -91,10 +191,11 @@ class PackageUpdate2(AdCPBaseModel): ), ] = None buyer_ref: Annotated[str, Field(description="Buyer's reference for the package to update")] - catalog: Annotated[ - catalog_1.Catalog | None, + catalogs: Annotated[ + list[catalog.Catalog] | None, Field( - description='Update the catalog this package promotes. Replaces the current catalog reference.' + description='Replace the catalogs this package promotes. Uses replacement semantics — the provided array replaces the current list. Omit to leave catalogs unchanged.', + min_length=1, ), ] = None creative_assignments: Annotated[ @@ -115,7 +216,41 @@ class PackageUpdate2(AdCPBaseModel): impressions: Annotated[ float | None, Field(description='Updated impression goal for this package', ge=0.0) ] = None - optimization_goal: optimization_goal_1.OptimizationGoal | None = None + keyword_targets_add: Annotated[ + list[KeywordTargetsAddItem1] | None, + Field( + description='Keyword targets to add or update on this package. Upserts by (keyword, match_type) identity: if the pair already exists, its bid_price is updated; if not, a new keyword target is added. Use targeting_overlay.keyword_targets in create_media_buy to set the initial list.', + min_length=1, + ), + ] = None + keyword_targets_remove: Annotated[ + list[KeywordTargetsRemoveItem1] | None, + Field( + description='Keyword targets to remove from this package. Removes matching (keyword, match_type) pairs. If a specified pair is not present, sellers SHOULD treat it as a no-op for that entry.', + min_length=1, + ), + ] = None + negative_keywords_add: Annotated[ + list[NegativeKeywordsAddItem1] | None, + Field( + description='Negative keywords to add to this package. Appends to the existing negative keyword list — does not replace it. If a keyword+match_type pair already exists, sellers SHOULD treat it as a no-op for that entry. Use targeting_overlay.negative_keywords in create_media_buy to set the initial list.', + min_length=1, + ), + ] = None + negative_keywords_remove: Annotated[ + list[NegativeKeywordsRemoveItem1] | None, + Field( + description='Negative keywords to remove from this package. Removes matching keyword+match_type pairs from the existing list. If a specified pair is not present, sellers SHOULD treat it as a no-op for that entry.', + min_length=1, + ), + ] = None + optimization_goals: Annotated[ + list[optimization_goal.OptimizationGoal] | None, + Field( + description='Replace all optimization goals for this package. Uses replacement semantics — omit to leave goals unchanged.', + min_length=1, + ), + ] = None pacing: pacing_1.Pacing | None = None package_id: Annotated[str | None, Field(description="Publisher's ID of package to update")] = ( None @@ -124,7 +259,12 @@ class PackageUpdate2(AdCPBaseModel): bool | None, Field(description='Pause/resume specific package (true = paused, false = active)'), ] = None - targeting_overlay: targeting.TargetingOverlay | None = None + targeting_overlay: Annotated[ + targeting.TargetingOverlay | None, + Field( + description='Targeting overlay to apply to this package. Uses replacement semantics — the full overlay replaces the previous one. Omit to leave targeting unchanged. For keyword and negative keyword updates, prefer the incremental operations (keyword_targets_add, keyword_targets_remove, negative_keywords_add, negative_keywords_remove) which avoid replacing the full overlay. Sellers SHOULD return a validation error if targeting_overlay.keyword_targets is present in the same request as keyword_targets_add or keyword_targets_remove, and likewise for negative_keywords.' + ), + ] = None class PackageUpdate(RootModel[PackageUpdate1 | PackageUpdate2]): diff --git a/src/adcp/types/generated_poc/media_buy/provide_performance_feedback_request.py b/src/adcp/types/generated_poc/media_buy/provide_performance_feedback_request.py index 2616fe19..da799011 100644 --- a/src/adcp/types/generated_poc/media_buy/provide_performance_feedback_request.py +++ b/src/adcp/types/generated_poc/media_buy/provide_performance_feedback_request.py @@ -1,32 +1,21 @@ # generated by datamodel-codegen: # filename: media_buy/provide_performance_feedback_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations from typing import Annotated from adcp.types.base import AdCPBaseModel -from pydantic import AwareDatetime, ConfigDict, Field, RootModel +from pydantic import ConfigDict, Field, RootModel from ..core import context as context_1 +from ..core import datetime_range from ..core import ext as ext_1 from ..enums import feedback_source as feedback_source_1 from ..enums import metric_type as metric_type_1 -class MeasurementPeriod(AdCPBaseModel): - model_config = ConfigDict( - extra='allow', - ) - end: Annotated[ - AwareDatetime, Field(description='ISO 8601 end timestamp for measurement period') - ] - start: Annotated[ - AwareDatetime, Field(description='ISO 8601 start timestamp for measurement period') - ] - - class ProvidePerformanceFeedbackRequest1(AdCPBaseModel): model_config = ConfigDict( extra='allow', @@ -46,7 +35,7 @@ class ProvidePerformanceFeedbackRequest1(AdCPBaseModel): feedback_source_1.FeedbackSource | None, Field(description='Source of the performance data') ] = feedback_source_1.FeedbackSource.buyer_attribution measurement_period: Annotated[ - MeasurementPeriod, Field(description='Time period for performance measurement') + datetime_range.DatetimeRange, Field(description='Time period for performance measurement') ] media_buy_id: Annotated[ str, Field(description="Publisher's media buy identifier", min_length=1) @@ -89,7 +78,7 @@ class ProvidePerformanceFeedbackRequest2(AdCPBaseModel): feedback_source_1.FeedbackSource | None, Field(description='Source of the performance data') ] = feedback_source_1.FeedbackSource.buyer_attribution measurement_period: Annotated[ - MeasurementPeriod, Field(description='Time period for performance measurement') + datetime_range.DatetimeRange, Field(description='Time period for performance measurement') ] media_buy_id: Annotated[ str | None, Field(description="Publisher's media buy identifier", min_length=1) diff --git a/src/adcp/types/generated_poc/media_buy/sync_audiences_request.py b/src/adcp/types/generated_poc/media_buy/sync_audiences_request.py index 1ad8eed7..60ca32ed 100644 --- a/src/adcp/types/generated_poc/media_buy/sync_audiences_request.py +++ b/src/adcp/types/generated_poc/media_buy/sync_audiences_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/sync_audiences_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -8,18 +8,22 @@ from typing import Annotated from adcp.types.base import AdCPBaseModel -from pydantic import ConfigDict, Field +from pydantic import ConfigDict, Field, RootModel -from ..core import audience_member +from ..core import account_ref, audience_member from ..core import context as context_1 from ..core import ext as ext_1 +from ..enums import consent_basis as consent_basis_1 -class ConsentBasis(Enum): - consent = 'consent' - legitimate_interest = 'legitimate_interest' - contract = 'contract' - legal_obligation = 'legal_obligation' +class AudienceType(Enum): + crm = 'crm' + suppression = 'suppression' + lookalike_seed = 'lookalike_seed' + + +class Tag(RootModel[str]): + root: Annotated[str, Field(min_length=1)] class Audience(AdCPBaseModel): @@ -39,8 +43,14 @@ class Audience(AdCPBaseModel): description="Buyer's identifier for this audience. Used to reference the audience in targeting overlays." ), ] + audience_type: Annotated[ + AudienceType | None, + Field( + description="Intended use for this audience. 'crm': target these users. 'suppression': exclude these users from delivery. 'lookalike_seed': use as a seed for the seller's lookalike modeling. Sellers may handle audiences differently based on type (e.g., suppression lists bypass minimum size requirements on some platforms)." + ), + ] = None consent_basis: Annotated[ - ConsentBasis | None, + consent_basis_1.ConsentBasis | None, Field( description='GDPR lawful basis for processing this audience list. Informational — not validated by the protocol, but required by some sellers operating in regulated markets (e.g. EU). When omitted, the buyer asserts they have a lawful basis appropriate to their jurisdiction.' ), @@ -51,6 +61,12 @@ class Audience(AdCPBaseModel): description='When true, delete this audience from the account entirely. All other fields on this audience object are ignored. Use this to delete a specific audience without affecting others.' ), ] = None + description: Annotated[ + str | None, + Field( + description="Human-readable description of this audience's composition or purpose (e.g., 'High-value customers who purchased in the last 90 days')." + ), + ] = None name: Annotated[str | None, Field(description='Human-readable name for this audience')] = None remove: Annotated[ list[audience_member.AudienceMember] | None, @@ -59,13 +75,21 @@ class Audience(AdCPBaseModel): min_length=1, ), ] = None + tags: Annotated[ + list[Tag] | None, + Field( + description="Buyer-defined tags for organizing and filtering audiences (e.g., 'holiday_2026', 'high_ltv'). Tags are stored by the seller and returned in discovery-only calls." + ), + ] = None class SyncAudiencesRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_id: Annotated[str, Field(description='Account to manage audiences for')] + account: Annotated[ + account_ref.AccountReference, Field(description='Account to manage audiences for.') + ] audiences: Annotated[ list[Audience] | None, Field( diff --git a/src/adcp/types/generated_poc/media_buy/sync_audiences_response.py b/src/adcp/types/generated_poc/media_buy/sync_audiences_response.py index f0a7d163..aca5f5f0 100644 --- a/src/adcp/types/generated_poc/media_buy/sync_audiences_response.py +++ b/src/adcp/types/generated_poc/media_buy/sync_audiences_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/sync_audiences_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -77,6 +77,13 @@ class Audience(AdCPBaseModel): description="Matching status. Present when action is created, updated, or unchanged; absent when action is deleted or failed. 'processing': platform is still matching members against its user base. 'ready': audience is available for targeting, matched_count is populated. 'too_small': matched audience is below the platform's minimum size — add more members and re-sync." ), ] = None + total_uploaded_count: Annotated[ + int | None, + Field( + description='Cumulative number of members uploaded across all syncs for this audience. Compare with matched_count to calculate match rate (matched_count / total_uploaded_count). Populated when the seller tracks cumulative upload counts.', + ge=0, + ), + ] = None uploaded_count: Annotated[ int | None, Field( diff --git a/src/adcp/types/generated_poc/media_buy/sync_catalogs_request.py b/src/adcp/types/generated_poc/media_buy/sync_catalogs_request.py index ba3ed0c4..0bf02417 100644 --- a/src/adcp/types/generated_poc/media_buy/sync_catalogs_request.py +++ b/src/adcp/types/generated_poc/media_buy/sync_catalogs_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/sync_catalogs_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -9,7 +9,7 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field -from ..core import catalog +from ..core import account_ref, catalog from ..core import context as context_1 from ..core import ext as ext_1 from ..core import push_notification_config as push_notification_config_1 @@ -20,12 +20,9 @@ class SyncCatalogsRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_id: Annotated[ - str | None, - Field( - description='Account that owns these catalogs. Required if the agent has multiple accounts and the seller cannot route automatically.' - ), - ] = None + account: Annotated[ + account_ref.AccountReference, Field(description='Account that owns these catalogs.') + ] catalog_ids: Annotated[ list[str] | None, Field( diff --git a/src/adcp/types/generated_poc/media_buy/sync_creatives_request.py b/src/adcp/types/generated_poc/media_buy/sync_creatives_request.py index d10aec26..c32883c4 100644 --- a/src/adcp/types/generated_poc/media_buy/sync_creatives_request.py +++ b/src/adcp/types/generated_poc/media_buy/sync_creatives_request.py @@ -1,14 +1,15 @@ # generated by datamodel-codegen: # filename: media_buy/sync_creatives_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations from typing import Annotated from adcp.types.base import AdCPBaseModel -from pydantic import ConfigDict, Field, StringConstraints +from pydantic import ConfigDict, Field +from ..core import account_ref from ..core import context as context_1 from ..core import creative_asset from ..core import ext as ext_1 @@ -16,19 +17,42 @@ from ..enums import validation_mode as validation_mode_1 -class SyncCreativesRequest(AdCPBaseModel): +class Assignment(AdCPBaseModel): model_config = ConfigDict( - extra='allow', + extra='forbid', ) - account_id: Annotated[ - str | None, + creative_id: Annotated[str, Field(description='ID of the creative to assign')] + package_id: Annotated[str, Field(description='ID of the package to assign the creative to')] + placement_ids: Annotated[ + list[str] | None, Field( - description='Account that owns these creatives. Optional if the agent has a single account or the seller can determine the account from context. Required if the agent has multiple accounts and the seller cannot route automatically.' + description='Restrict this creative to specific placements within the package. When omitted, the creative is eligible for all placements.', + min_length=1, ), ] = None + weight: Annotated[ + float | None, + Field( + description='Relative delivery weight (0–100). When multiple creatives are assigned to the same package, weights determine impression distribution proportionally — a creative with weight 2 gets twice the delivery of weight 1. When omitted, the creative receives equal rotation with other unweighted creatives. A weight of 0 means the creative is assigned but paused (receives no delivery).', + ge=0.0, + le=100.0, + ), + ] = None + + +class SyncCreativesRequest(AdCPBaseModel): + model_config = ConfigDict( + extra='allow', + ) + account: Annotated[ + account_ref.AccountReference, Field(description='Account that owns these creatives.') + ] assignments: Annotated[ - dict[Annotated[str, StringConstraints(pattern=r'^[a-zA-Z0-9_-]+$')], list[str]] | None, - Field(description='Optional bulk assignment of creatives to packages'), + list[Assignment] | None, + Field( + description='Optional bulk assignment of creatives to packages. Each entry maps one creative to one package with optional weight and placement targeting.', + min_length=1, + ), ] = None context: context_1.ContextObject | None = None creative_ids: Annotated[ @@ -60,6 +84,14 @@ class SyncCreativesRequest(AdCPBaseModel): ), ] = False ext: ext_1.ExtensionObject | None = None + idempotency_key: Annotated[ + str | None, + Field( + description='Client-generated idempotency key for safe retries. If a sync fails without a response, resending with the same idempotency_key guarantees at-most-once execution.', + max_length=255, + min_length=8, + ), + ] = None push_notification_config: Annotated[ push_notification_config_1.PushNotificationConfig | None, Field( diff --git a/src/adcp/types/generated_poc/media_buy/sync_event_sources_request.py b/src/adcp/types/generated_poc/media_buy/sync_event_sources_request.py index 14d03375..67d400a5 100644 --- a/src/adcp/types/generated_poc/media_buy/sync_event_sources_request.py +++ b/src/adcp/types/generated_poc/media_buy/sync_event_sources_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/sync_event_sources_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-27T02:10:10+00:00 from __future__ import annotations @@ -9,6 +9,7 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field +from ..core import account_ref from ..core import context as context_1 from ..core import ext as ext_1 from ..enums import event_type @@ -39,7 +40,9 @@ class SyncEventSourcesRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - account_id: Annotated[str, Field(description='Account to configure event sources for')] + account: Annotated[ + account_ref.AccountReference, Field(description='Account to configure event sources for.') + ] context: context_1.ContextObject | None = None delete_missing: Annotated[ bool | None, diff --git a/src/adcp/types/generated_poc/media_buy/update_media_buy_request.py b/src/adcp/types/generated_poc/media_buy/update_media_buy_request.py index 1bc64fdc..32e7bdcc 100644 --- a/src/adcp/types/generated_poc/media_buy/update_media_buy_request.py +++ b/src/adcp/types/generated_poc/media_buy/update_media_buy_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: media_buy/update_media_buy_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -29,6 +29,14 @@ class UpdateMediaBuyRequest1(AdCPBaseModel): AwareDatetime | None, Field(description='New end date/time in ISO 8601 format') ] = None ext: ext_1.ExtensionObject | None = None + idempotency_key: Annotated[ + str | None, + Field( + description='Client-generated idempotency key for safe retries. If an update fails without a response, resending with the same idempotency_key guarantees the update is applied at most once.', + max_length=255, + min_length=8, + ), + ] = None media_buy_id: Annotated[str, Field(description="Publisher's ID of the media buy to update")] packages: Annotated[ list[package_update.PackageUpdate] | None, @@ -63,6 +71,14 @@ class UpdateMediaBuyRequest2(AdCPBaseModel): AwareDatetime | None, Field(description='New end date/time in ISO 8601 format') ] = None ext: ext_1.ExtensionObject | None = None + idempotency_key: Annotated[ + str | None, + Field( + description='Client-generated idempotency key for safe retries. If an update fails without a response, resending with the same idempotency_key guarantees the update is applied at most once.', + max_length=255, + min_length=8, + ), + ] = None media_buy_id: Annotated[ str | None, Field(description="Publisher's ID of the media buy to update") ] = None diff --git a/src/adcp/types/generated_poc/property/property_list_filters.py b/src/adcp/types/generated_poc/property/property_list_filters.py index 332e9187..e3a1e7b3 100644 --- a/src/adcp/types/generated_poc/property/property_list_filters.py +++ b/src/adcp/types/generated_poc/property/property_list_filters.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: property/property_list_filters.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -23,18 +23,19 @@ class PropertyListFilters(AdCPBaseModel): extra='forbid', ) channels_any: Annotated[ - list[channels.MediaChannel], + list[channels.MediaChannel] | None, Field( - description='Property must support ANY of the listed channels. Required.', min_length=1 + description='Property must support ANY of the listed channels. When omitted, no channel restriction is applied.', + min_length=1, ), - ] + ] = None countries_all: Annotated[ - list[CountriesAllItem], + list[CountriesAllItem] | None, Field( - description='Property must have feature data for ALL listed countries (ISO codes). Required.', + description='Property must have feature data for ALL listed countries (ISO codes). When omitted, no country restriction is applied.', min_length=1, ), - ] + ] = None exclude_identifiers: Annotated[ list[identifier.Identifier] | None, Field(description='Identifiers to always exclude from results', min_length=1), diff --git a/src/adcp/types/generated_poc/protocol/get_adcp_capabilities_response.py b/src/adcp/types/generated_poc/protocol/get_adcp_capabilities_response.py index 8df72c23..932cf546 100644 --- a/src/adcp/types/generated_poc/protocol/get_adcp_capabilities_response.py +++ b/src/adcp/types/generated_poc/protocol/get_adcp_capabilities_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: protocol/get_adcp_capabilities_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -11,34 +11,44 @@ from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, RootModel from ..core import context as context_1 -from ..core import error +from ..core import duration, error from ..core import ext as ext_1 from ..core import media_buy_features from ..enums import action_source, age_verification_method, channels from ..enums import event_type as event_type_1 -from ..enums import uid_type +from ..enums import pricing_model, transport_mode, uid_type from ..sponsored_intelligence import si_capabilities -class DefaultBilling(Enum): - brand = 'brand' +class AccountResolution(Enum): + explicit_account_id = 'explicit_account_id' + implicit_from_sync = 'implicit_from_sync' + + +class SupportedBillingEnum(Enum): operator = 'operator' agent = 'agent' class Account(AdCPBaseModel): + account_financials: Annotated[ + bool | None, + Field( + description='Whether this seller supports the get_account_financials task for querying account-level financial status (spend, credit, invoices). Only applicable to operator-billed accounts.' + ), + ] = False + account_resolution: Annotated[ + AccountResolution | None, + Field( + description='How the seller resolves account references. explicit_account_id: accounts are managed out-of-band (advertiser portal, sales rep) and discovered via list_accounts. implicit_from_sync: buyer declares intent via sync_accounts and the seller provisions accounts.' + ), + ] = AccountResolution.explicit_account_id authorization_endpoint: Annotated[ AnyUrl | None, Field( description='OAuth authorization endpoint for obtaining operator-level credentials. Present when the seller supports OAuth for operator authentication. The agent directs the operator to this URL to authenticate and obtain a bearer token. If absent and require_operator_auth is true, operators obtain credentials out-of-band (e.g., seller portal, API key).' ), ] = None - default_billing: Annotated[ - DefaultBilling | None, - Field( - description='The billing model applied when the agent omits billing from a sync_accounts request. Must be one of the values in supported_billing.' - ), - ] = None require_operator_auth: Annotated[ bool | None, Field( @@ -48,13 +58,13 @@ class Account(AdCPBaseModel): required_for_products: Annotated[ bool | None, Field( - description='Whether an active account is required to call get_products. When true, the agent must establish an account via sync_accounts before browsing products. When false, get_products works without an account (account_id is optional for rate-card-specific pricing).' + description='Whether an account reference is required for get_products. When true, the buyer must establish an account before browsing products. When false (default), the buyer can browse products without an account — useful for price comparison and discovery before committing to a seller.' ), ] = False supported_billing: Annotated[ - list[DefaultBilling], + list[SupportedBillingEnum], Field( - description='Billing models this seller supports. brand: seller invoices the brand directly. operator: seller invoices the operator (agency). agent: agent consolidates billing.', + description='Billing models this seller supports. operator: seller invoices the operator (agency or brand buying direct). agent: agent consolidates billing. The buyer must pass one of these values in sync_accounts.', min_length=1, ), ] @@ -68,7 +78,7 @@ class Adcp(AdCPBaseModel): major_versions: Annotated[ list[MajorVersion], Field( - description='AdCP major versions supported by this seller. Major versions indicate breaking changes.', + description='AdCP major versions supported by this seller. Major versions indicate breaking changes. When multiple versions are listed, the buyer declares its version during the capabilities handshake or via the adcp_version field on requests.', min_length=1, ), ] @@ -78,10 +88,10 @@ class Creative(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - supports_brief: Annotated[ + supports_compliance: Annotated[ bool | None, Field( - description='Whether this creative agent accepts creative_brief in build_creative requests for structured campaign-level creative direction' + description='When true, this creative agent can process briefs with compliance requirements (required_disclosures, prohibited_claims) and will validate that disclosures can be satisfied by the target format.' ), ] = None @@ -199,21 +209,21 @@ class AttributionWindow(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) - click_through: Annotated[ - list[str], - Field( - description='Available click-through attribution windows (e.g. ["7d"], ["7d", "14d", "30d"])', - min_length=1, - ), - ] event_type: Annotated[ event_type_1.EventType | None, Field(description='Event type this window applies to, or omit for default window'), ] = None - view_through: Annotated[ - list[str] | None, + post_click: Annotated[ + list[duration.Duration], Field( - description='Available view-through attribution windows (e.g. ["1d"], ["1d", "7d", "14d"])', + description='Available post-click attribution windows (e.g. [{"interval": 7, "unit": "days"}])', + min_length=1, + ), + ] + post_view: Annotated[ + list[duration.Duration] | None, + Field( + description='Available post-view attribution windows (e.g. [{"interval": 1, "unit": "days"}])', min_length=1, ), ] = None @@ -266,6 +276,9 @@ class GeoMetros(AdCPBaseModel): class GeoPostalAreas(AdCPBaseModel): + at_plz: Annotated[ + bool | None, Field(description="Austrian Postleitzahl, 4 digits (e.g., '1010')") + ] = None au_postcode: Annotated[ bool | None, Field(description="Australian postcode, 4 digits (e.g., '2000')") ] = None @@ -275,6 +288,9 @@ class GeoPostalAreas(AdCPBaseModel): ca_full: Annotated[ bool | None, Field(description="Canadian full postal code (e.g., 'K1A 0B1')") ] = None + ch_plz: Annotated[ + bool | None, Field(description="Swiss Postleitzahl, 4 digits (e.g., '8000')") + ] = None de_plz: Annotated[ bool | None, Field(description="German Postleitzahl, 5 digits (e.g., '10115')") ] = None @@ -291,75 +307,30 @@ class GeoPostalAreas(AdCPBaseModel): ] = None -class Targeting(AdCPBaseModel): - age_restriction: Annotated[ - AgeRestriction | None, - Field(description='Age restriction capabilities for compliance (alcohol, gambling)'), - ] = None - audience_exclude: Annotated[ - bool | None, - Field( - description='Whether seller supports audience_exclude in targeting overlays (requires features.audience_targeting)' - ), - ] = None - audience_include: Annotated[ - bool | None, - Field( - description='Whether seller supports audience_include in targeting overlays (requires features.audience_targeting)' - ), - ] = None - device_platform: Annotated[ - bool | None, - Field( - description='Whether seller supports device platform targeting (Sec-CH-UA-Platform values)' - ), - ] = None - geo_countries: Annotated[ - bool | None, - Field( - description="Supports country-level geo targeting using ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE')" - ), - ] = None - geo_metros: Annotated[ - GeoMetros | None, - Field( - description='Metro area targeting support. Specifies which classification systems are supported.' - ), - ] = None - geo_postal_areas: Annotated[ - GeoPostalAreas | None, - Field( - description='Postal area targeting support. Specifies which postal code systems are supported. System names encode country and precision.' - ), - ] = None - geo_regions: Annotated[ - bool | None, - Field( - description="Supports region/state-level geo targeting using ISO 3166-2 subdivision codes (e.g., 'US-NY', 'GB-SCT', 'DE-BY')" - ), - ] = None - language: Annotated[ - bool | None, - Field(description='Whether seller supports language targeting (ISO 639-1 codes)'), - ] = None +class SupportedMatchType(Enum): + broad = 'broad' + phrase = 'phrase' + exact = 'exact' -class Execution(AdCPBaseModel): - axe_integrations: Annotated[ - list[AnyUrl] | None, +class KeywordTargets(AdCPBaseModel): + supported_match_types: Annotated[ + list[SupportedMatchType], Field( - description='Agentic ad exchange (AXE) integrations supported. URLs are canonical identifiers for exchanges this seller can execute through.' + description='Match types this seller supports for keyword targets. Sellers must reject goals with unsupported match types.', + min_length=1, ), - ] = None - creative_specs: Annotated[ - CreativeSpecs | None, Field(description='Creative specification support') - ] = None - targeting: Annotated[ - Targeting | None, + ] + + +class NegativeKeywords(AdCPBaseModel): + supported_match_types: Annotated[ + list[SupportedMatchType], Field( - description='Targeting capabilities. If declared true/supported, buyer can use these targeting parameters and seller MUST honor them.' + description='Match types this seller supports for negative keywords. Sellers must reject goals with unsupported match types.', + min_length=1, ), - ] = None + ] class PrimaryCountry(RootModel[str]): @@ -403,6 +374,42 @@ class Portfolio(AdCPBaseModel): ] +class AvailableDimension(Enum): + geo = 'geo' + device_type = 'device_type' + device_platform = 'device_platform' + audience = 'audience' + placement = 'placement' + creative = 'creative' + keyword = 'keyword' + catalog_item = 'catalog_item' + + +class Reporting(AdCPBaseModel): + available_dimensions: Annotated[ + list[AvailableDimension] | None, + Field( + description="Reporting dimensions available across the seller's portfolio. Individual products may support a subset. Dimensions geo, device_type, device_platform, audience, and placement are opt-in via reporting_dimensions on the delivery request. Dimensions creative, keyword, and catalog_item are included automatically when the seller supports them (not controlled by reporting_dimensions)." + ), + ] = None + supports_daily_breakdown: Annotated[ + bool | None, + Field( + description='Whether delivery reporting includes daily_breakdown at the media buy and/or package level.' + ), + ] = None + supports_date_range: Annotated[ + bool | None, + Field( + description="Whether any products support date range filtering (date_range_support: 'date_range'). When false, all products return lifetime-only data." + ), + ] = None + supports_webhooks: Annotated[ + bool | None, + Field(description='Whether any products support webhook-based reporting notifications.'), + ] = None + + class DataProviderDomain(PublisherDomain): pass @@ -483,7 +490,7 @@ class AudienceTargeting(AdCPBaseModel): supported_identifier_types: Annotated[ list[SupportedIdentifierType], Field( - description='Hashed PII types accepted for audience matching. Buyers should only send identifiers the seller supports.', + description='PII-derived identifier types accepted for audience matching. Buyers should only send identifiers the seller supports.', min_length=1, ), ] @@ -494,6 +501,12 @@ class AudienceTargeting(AdCPBaseModel): min_length=1, ), ] = None + supports_platform_customer_id: Annotated[ + bool | None, + Field( + description="Whether the seller accepts the buyer's CRM/loyalty ID as a matchable identifier. Only applicable when the seller operates a closed ecosystem with a shared ID namespace (e.g., a retailer matching against their loyalty program). When true, buyers can include platform_customer_id values in AudienceMember.identifiers for matching against the seller's identity graph. Reporting on matched platform_customer_ids typically requires a clean room or the seller's own reporting surface." + ), + ] = None class ConversionTracking(AdCPBaseModel): @@ -503,7 +516,13 @@ class ConversionTracking(AdCPBaseModel): attribution_windows: Annotated[ list[AttributionWindow] | None, Field( - description='Attribution windows available from this seller. Single-element arrays indicate fixed windows; multi-element arrays indicate configurable options the buyer can choose from via optimization_goal.attribution_window on packages.' + description='Attribution windows available from this seller. Single-element arrays indicate fixed windows; multi-element arrays indicate configurable options the buyer can choose from via attribution_window on optimization goals.' + ), + ] = None + multi_source_event_dedup: Annotated[ + bool | None, + Field( + description='Whether this seller can deduplicate conversion events across multiple event sources within a single goal. When true, the seller honors the deduplication semantics in optimization_goals event_sources arrays — the same event_id from multiple sources counts once. When false or absent, buyers should use a single event source per goal; multi-source arrays will be treated as first-source-wins. Most social platforms cannot deduplicate across independently-managed pixel and CAPI sources.' ), ] = None supported_action_sources: Annotated[ @@ -530,6 +549,129 @@ class ConversionTracking(AdCPBaseModel): ] = None +class GeoProximity(AdCPBaseModel): + geometry: Annotated[ + bool | None, + Field( + description='Whether seller supports pre-computed GeoJSON geometry (buyer provides the polygon)' + ), + ] = None + radius: Annotated[ + bool | None, + Field( + description='Whether seller supports simple radius targeting (distance circle from a point)' + ), + ] = None + transport_modes: Annotated[ + list[transport_mode.TransportMode] | None, + Field( + description='Transport modes supported for travel_time isochrones. Only relevant when travel_time is true.', + min_length=1, + ), + ] = None + travel_time: Annotated[ + bool | None, + Field( + description='Whether seller supports travel time isochrone targeting (requires a routing engine)' + ), + ] = None + + +class Targeting(AdCPBaseModel): + age_restriction: Annotated[ + AgeRestriction | None, + Field(description='Age restriction capabilities for compliance (alcohol, gambling)'), + ] = None + audience_exclude: Annotated[ + bool | None, + Field( + description='Whether seller supports audience_exclude in targeting overlays (requires features.audience_targeting)' + ), + ] = None + audience_include: Annotated[ + bool | None, + Field( + description='Whether seller supports audience_include in targeting overlays (requires features.audience_targeting)' + ), + ] = None + device_platform: Annotated[ + bool | None, + Field( + description='Whether seller supports device platform targeting (Sec-CH-UA-Platform values)' + ), + ] = None + device_type: Annotated[ + bool | None, + Field( + description='Whether seller supports device type targeting (form factor: desktop, mobile, tablet, ctv, dooh, unknown). When true, seller supports both device_type (include) and device_type_exclude (exclude) in targeting overlays.' + ), + ] = None + geo_countries: Annotated[ + bool | None, + Field( + description="Supports country-level geo targeting using ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE')" + ), + ] = None + geo_metros: Annotated[ + GeoMetros | None, + Field( + description='Metro area targeting support. Specifies which classification systems are supported.' + ), + ] = None + geo_postal_areas: Annotated[ + GeoPostalAreas | None, + Field( + description='Postal area targeting support. Specifies which postal code systems are supported. System names encode country and precision.' + ), + ] = None + geo_proximity: Annotated[ + GeoProximity | None, + Field( + description='Proximity targeting capabilities from arbitrary coordinates via targeting_overlay.geo_proximity.' + ), + ] = None + geo_regions: Annotated[ + bool | None, + Field( + description="Supports region/state-level geo targeting using ISO 3166-2 subdivision codes (e.g., 'US-NY', 'GB-SCT', 'DE-BY')" + ), + ] = None + keyword_targets: Annotated[ + KeywordTargets | None, + Field( + description='Keyword targeting capabilities. Presence indicates support for targeting_overlay.keyword_targets and keyword_targets_add/remove in update_media_buy.' + ), + ] = None + language: Annotated[ + bool | None, + Field(description='Whether seller supports language targeting (ISO 639-1 codes)'), + ] = None + negative_keywords: Annotated[ + NegativeKeywords | None, + Field( + description='Negative keyword capabilities. Presence indicates support for targeting_overlay.negative_keywords and negative_keywords_add/remove in update_media_buy.' + ), + ] = None + + +class Execution(AdCPBaseModel): + axe_integrations: Annotated[ + list[AnyUrl] | None, + Field( + description='Agentic ad exchange (AXE) integrations supported. URLs are canonical identifiers for exchanges this seller can execute through.' + ), + ] = None + creative_specs: Annotated[ + CreativeSpecs | None, Field(description='Creative specification support') + ] = None + targeting: Annotated[ + Targeting | None, + Field( + description='Targeting capabilities. If declared true/supported, buyer can use these targeting parameters and seller MUST honor them.' + ), + ] = None + + class MediaBuy(AdCPBaseModel): audience_targeting: Annotated[ AudienceTargeting | None, @@ -551,6 +693,19 @@ class MediaBuy(AdCPBaseModel): Portfolio | None, Field(description="Information about the seller's media inventory portfolio"), ] = None + reporting: Annotated[ + Reporting | None, + Field( + description="Seller-level reporting capabilities. Summarizes what reporting features are available across the seller's product portfolio. Individual products may vary — check product-level reporting_capabilities for specifics." + ), + ] = None + supported_pricing_models: Annotated[ + list[pricing_model.PricingModel] | None, + Field( + description='Pricing models this seller supports across its product portfolio. Buyers can use this for pre-flight filtering before querying individual products. Individual products may support a subset of these models.', + min_length=1, + ), + ] = None class SponsoredIntelligence(AdCPBaseModel): diff --git a/src/adcp/types/generated_poc/signals/activate_signal_request.py b/src/adcp/types/generated_poc/signals/activate_signal_request.py index d5dd77af..66685bd0 100644 --- a/src/adcp/types/generated_poc/signals/activate_signal_request.py +++ b/src/adcp/types/generated_poc/signals/activate_signal_request.py @@ -1,32 +1,63 @@ # generated by datamodel-codegen: # filename: signals/activate_signal_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations +from enum import Enum from typing import Annotated from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field +from ..core import account_ref from ..core import context as context_1 from ..core import destination from ..core import ext as ext_1 +class Action(Enum): + activate = 'activate' + deactivate = 'deactivate' + + class ActivateSignalRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) + account: Annotated[ + account_ref.AccountReference | None, + Field( + description='Account for this activation. Associates with a commercial relationship established via sync_accounts.' + ), + ] = None + action: Annotated[ + Action | None, + Field( + description="Whether to activate or deactivate the signal. Deactivating removes the segment from downstream platforms, required when campaigns end to comply with data governance policies (GDPR, CCPA). Defaults to 'activate' when omitted." + ), + ] = Action.activate + buyer_campaign_ref: Annotated[ + str | None, + Field( + description="The buyer's campaign reference for this activation. Enables the signals agent to correlate activations with subsequent report_usage calls." + ), + ] = None context: context_1.ContextObject | None = None - deployments: Annotated[ + destinations: Annotated[ list[destination.Destination], Field( - description='Target deployment(s) for activation. If the authenticated caller matches one of these deployment targets, activation keys will be included in the response.', + description='Target destination(s) for activation. If the authenticated caller matches one of these destinations, activation keys will be included in the response.', min_length=1, ), ] ext: ext_1.ExtensionObject | None = None + pricing_option_id: Annotated[ + str | None, + Field( + description="The pricing option selected from the signal's pricing_options in the get_signals response. Required when the signal has pricing options. Records the buyer's pricing commitment at activation time; pass this same value in report_usage for billing verification." + ), + ] = None signal_agent_segment_id: Annotated[ str, Field(description='The universal identifier for the signal to activate') ] diff --git a/src/adcp/types/generated_poc/signals/get_signals_request.py b/src/adcp/types/generated_poc/signals/get_signals_request.py index fbd23a06..2e4d3220 100644 --- a/src/adcp/types/generated_poc/signals/get_signals_request.py +++ b/src/adcp/types/generated_poc/signals/get_signals_request.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: signals/get_signals_request.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -9,6 +9,7 @@ from adcp.types.base import AdCPBaseModel from pydantic import ConfigDict, Field, RootModel +from ..core import account_ref from ..core import context as context_1 from ..core import destination from ..core import ext as ext_1 @@ -19,35 +20,37 @@ class Country(RootModel[str]): root: Annotated[str, Field(pattern='^[A-Z]{2}$')] -class DeliverTo(AdCPBaseModel): +class GetSignalsRequest1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) + account: Annotated[ + account_ref.AccountReference | None, + Field( + description='Account for this request. When provided, the signals agent returns per-account pricing options if configured.' + ), + ] = None + buyer_campaign_ref: Annotated[ + str | None, + Field( + description="The buyer's campaign reference. Used to correlate signal discovery with subsequent report_usage calls." + ), + ] = None + context: context_1.ContextObject | None = None countries: Annotated[ - list[Country], - Field(description='Countries where signals will be used (ISO codes)', min_length=1), - ] - deployments: Annotated[ - list[destination.Destination], + list[Country] | None, Field( - description='List of deployment targets (DSPs, sales agents, etc.). If the authenticated caller matches one of these deployment targets, activation keys will be included in the response.', + description='Countries where signals will be used (ISO 3166-1 alpha-2 codes). When omitted, no geographic filter is applied.', min_length=1, ), - ] - - -class DeliverTo1(DeliverTo): - pass - - -class GetSignalsRequest1(AdCPBaseModel): - model_config = ConfigDict( - extra='allow', - ) - context: context_1.ContextObject | None = None - deliver_to: Annotated[ - DeliverTo, Field(description='Deployment targets where signals need to be activated') - ] + ] = None + destinations: Annotated[ + list[destination.Destination] | None, + Field( + description='Filter signals to those activatable on specific agents/platforms. When omitted, returns all signals available on the current agent. If the authenticated caller matches one of these destinations, activation keys will be included in the response.', + min_length=1, + ), + ] = None ext: ext_1.ExtensionObject | None = None filters: signal_filters.SignalFilters | None = None max_results: Annotated[ @@ -57,7 +60,7 @@ class GetSignalsRequest1(AdCPBaseModel): signal_ids: Annotated[ list[signal_id.SignalId] | None, Field( - description="Specific signals to look up by data provider and ID. Returns exact matches from the data provider's catalog. Takes precedence over signal_spec when both are provided.", + description="Specific signals to look up by data provider and ID. Returns exact matches from the data provider's catalog. When combined with signal_spec, these signals anchor the starting set and signal_spec guides adjustments.", min_length=1, ), ] = None @@ -73,10 +76,33 @@ class GetSignalsRequest2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) + account: Annotated[ + account_ref.AccountReference | None, + Field( + description='Account for this request. When provided, the signals agent returns per-account pricing options if configured.' + ), + ] = None + buyer_campaign_ref: Annotated[ + str | None, + Field( + description="The buyer's campaign reference. Used to correlate signal discovery with subsequent report_usage calls." + ), + ] = None context: context_1.ContextObject | None = None - deliver_to: Annotated[ - DeliverTo1, Field(description='Deployment targets where signals need to be activated') - ] + countries: Annotated[ + list[Country] | None, + Field( + description='Countries where signals will be used (ISO 3166-1 alpha-2 codes). When omitted, no geographic filter is applied.', + min_length=1, + ), + ] = None + destinations: Annotated[ + list[destination.Destination] | None, + Field( + description='Filter signals to those activatable on specific agents/platforms. When omitted, returns all signals available on the current agent. If the authenticated caller matches one of these destinations, activation keys will be included in the response.', + min_length=1, + ), + ] = None ext: ext_1.ExtensionObject | None = None filters: signal_filters.SignalFilters | None = None max_results: Annotated[ @@ -86,7 +112,7 @@ class GetSignalsRequest2(AdCPBaseModel): signal_ids: Annotated[ list[signal_id.SignalId], Field( - description="Specific signals to look up by data provider and ID. Returns exact matches from the data provider's catalog. Takes precedence over signal_spec when both are provided.", + description="Specific signals to look up by data provider and ID. Returns exact matches from the data provider's catalog. When combined with signal_spec, these signals anchor the starting set and signal_spec guides adjustments.", min_length=1, ), ] @@ -102,7 +128,7 @@ class GetSignalsRequest(RootModel[GetSignalsRequest1 | GetSignalsRequest2]): root: Annotated[ GetSignalsRequest1 | GetSignalsRequest2, Field( - description='Request parameters for discovering signals. Use signal_spec for natural language discovery, signal_ids for exact lookups, or both (signal_ids take precedence for exact matches, signal_spec provides additional discovery context).', + description='Request parameters for discovering and refining signals. Use signal_spec for natural language discovery, signal_ids for exact lookups, or both to refine previous results (signal_ids anchor the starting set, signal_spec guides adjustments).', title='Get Signals Request', ), ] diff --git a/src/adcp/types/generated_poc/signals/get_signals_response.py b/src/adcp/types/generated_poc/signals/get_signals_response.py index 93407daa..a130c3a9 100644 --- a/src/adcp/types/generated_poc/signals/get_signals_response.py +++ b/src/adcp/types/generated_poc/signals/get_signals_response.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: signals/get_signals_response.json -# timestamp: 2026-02-23T01:56:40+00:00 +# timestamp: 2026-02-28T17:39:50+00:00 from __future__ import annotations @@ -14,21 +14,28 @@ from ..core import ext as ext_1 from ..core import pagination_response from ..core import signal_id as signal_id_1 +from ..core import signal_pricing_option from ..enums import signal_catalog_type, signal_value_type -class Pricing(AdCPBaseModel): +class Range(AdCPBaseModel): model_config = ConfigDict( - extra='allow', + extra='forbid', ) - cpm: Annotated[float, Field(description='Cost per thousand impressions', ge=0.0)] - currency: Annotated[str, Field(description='Currency code', pattern='^[A-Z]{3}$')] + max: Annotated[float, Field(description='Maximum value (inclusive)')] + min: Annotated[float, Field(description='Minimum value (inclusive)')] class Signal(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) + categories: Annotated[ + list[str] | None, + Field( + description="Valid values for categorical signals. Present when value_type is 'categorical'. Buyers must use one of these values in SignalTargeting.values." + ), + ] = None coverage_percentage: Annotated[ float, Field(description='Percentage of audience coverage', ge=0.0, le=100.0) ] @@ -38,7 +45,17 @@ class Signal(AdCPBaseModel): ] description: Annotated[str, Field(description='Detailed signal description')] name: Annotated[str, Field(description='Human-readable signal name')] - pricing: Annotated[Pricing, Field(description='Pricing information')] + pricing_options: Annotated[ + list[signal_pricing_option.SignalPricingOption], + Field( + description='Pricing options available for this signal. The buyer selects one and passes its pricing_option_id in report_usage for billing verification.', + min_length=1, + ), + ] + range: Annotated[ + Range | None, + Field(description="Valid range for numeric signals. Present when value_type is 'numeric'."), + ] = None signal_agent_segment_id: Annotated[ str, Field( diff --git a/src/adcp/utils/preview_cache.py b/src/adcp/utils/preview_cache.py index d84eeca9..c97136b0 100644 --- a/src/adcp/utils/preview_cache.py +++ b/src/adcp/utils/preview_cache.py @@ -67,7 +67,7 @@ async def get_preview_data_for_manifest( Returns: Preview data with preview_url and metadata, or None if generation fails """ - from adcp.types.aliases import PreviewCreativeFormatRequest + from adcp.types.aliases import PreviewCreativeSingleRequest cache_key = _make_manifest_cache_key(format_id, manifest.model_dump(exclude_none=True)) @@ -75,7 +75,7 @@ async def get_preview_data_for_manifest( return self._preview_cache[cache_key] try: - request = PreviewCreativeFormatRequest( + request = PreviewCreativeSingleRequest( request_type="single", format_id=format_id, creative_manifest=manifest, diff --git a/tests/test_backward_compat.py b/tests/test_backward_compat.py new file mode 100644 index 00000000..70118641 --- /dev/null +++ b/tests/test_backward_compat.py @@ -0,0 +1,237 @@ +"""Tests for backward compatibility aliases. + +Validates that deprecated type names still import and resolve to the correct +new types. This is the core safety net for the schema sync — consumers using +old names must not get ImportError or silently wrong types. +""" + +from __future__ import annotations + + +class TestPricingTypeRenames: + """Upstream merged auction/fixed variants into single types.""" + + def test_cpm_auction_imports_from_adcp(self): + from adcp import CpmAuctionPricingOption, CpmPricingOption + + assert CpmAuctionPricingOption is CpmPricingOption + + def test_cpm_fixed_rate_imports_from_adcp(self): + from adcp import CpmFixedRatePricingOption, CpmPricingOption + + assert CpmFixedRatePricingOption is CpmPricingOption + + def test_vcpm_auction_imports_from_adcp(self): + from adcp import VcpmAuctionPricingOption, VcpmPricingOption + + assert VcpmAuctionPricingOption is VcpmPricingOption + + def test_vcpm_fixed_rate_imports_from_adcp(self): + from adcp import VcpmFixedRatePricingOption, VcpmPricingOption + + assert VcpmFixedRatePricingOption is VcpmPricingOption + + def test_cpm_auction_imports_from_types(self): + from adcp.types import CpmAuctionPricingOption, CpmPricingOption + + assert CpmAuctionPricingOption is CpmPricingOption + + def test_vcpm_auction_imports_from_types(self): + from adcp.types import VcpmAuctionPricingOption, VcpmPricingOption + + assert VcpmAuctionPricingOption is VcpmPricingOption + + +class TestActivationKeyRenames: + """Upstream renamed property_id/property_tag to segment_id/key_value.""" + + def test_property_id_activation_key_imports(self): + from adcp import PropertyIdActivationKey, SegmentIdActivationKey + + assert PropertyIdActivationKey is SegmentIdActivationKey + + def test_property_tag_activation_key_imports(self): + from adcp import KeyValueActivationKey, PropertyTagActivationKey + + assert PropertyTagActivationKey is KeyValueActivationKey + + def test_property_id_imports_from_types(self): + from adcp.types import PropertyIdActivationKey, SegmentIdActivationKey + + assert PropertyIdActivationKey is SegmentIdActivationKey + + def test_property_tag_imports_from_types(self): + from adcp.types import KeyValueActivationKey, PropertyTagActivationKey + + assert PropertyTagActivationKey is KeyValueActivationKey + + +class TestPreviewAliasRenames: + """Upstream changed format/manifest → single/batch/variant discriminator.""" + + def test_preview_creative_format_request(self): + from adcp import PreviewCreativeFormatRequest, PreviewCreativeSingleRequest + + assert PreviewCreativeFormatRequest is PreviewCreativeSingleRequest + + def test_preview_creative_manifest_request(self): + from adcp import PreviewCreativeBatchRequest, PreviewCreativeManifestRequest + + assert PreviewCreativeManifestRequest is PreviewCreativeBatchRequest + + def test_preview_creative_static_response(self): + from adcp import PreviewCreativeSingleResponse, PreviewCreativeStaticResponse + + assert PreviewCreativeStaticResponse is PreviewCreativeSingleResponse + + def test_preview_creative_interactive_response(self): + from adcp import PreviewCreativeBatchResponse, PreviewCreativeInteractiveResponse + + assert PreviewCreativeInteractiveResponse is PreviewCreativeBatchResponse + + def test_format_request_from_types(self): + from adcp.types import PreviewCreativeFormatRequest, PreviewCreativeSingleRequest + + assert PreviewCreativeFormatRequest is PreviewCreativeSingleRequest + + def test_manifest_request_from_types(self): + from adcp.types import PreviewCreativeBatchRequest, PreviewCreativeManifestRequest + + assert PreviewCreativeManifestRequest is PreviewCreativeBatchRequest + + +class TestRemovedTypeStubs: + """Types removed from upstream schemas that have backward-compat stubs.""" + + def test_list_authorized_properties_request_imports(self): + from adcp import ListAuthorizedPropertiesRequest + + assert ListAuthorizedPropertiesRequest is not None + + def test_list_authorized_properties_response_imports(self): + from adcp import ListAuthorizedPropertiesResponse + + assert ListAuthorizedPropertiesResponse is not None + + def test_package_status_imports(self): + from adcp import PackageStatus + + assert PackageStatus is not None + + def test_deliver_to_imports(self): + from adcp import DeliverTo + + assert DeliverTo is not None + + def test_pricing_imports(self): + from adcp import Pricing + + assert Pricing is not None + + def test_measurement_imports(self): + from adcp import Measurement + from adcp.types import OutcomeMeasurement + + assert Measurement is OutcomeMeasurement + + def test_stubs_accept_extra_fields(self): + """Stubs should be open models that accept arbitrary fields.""" + from adcp import ListAuthorizedPropertiesRequest + + # These stubs should not raise on unknown fields + stub = ListAuthorizedPropertiesRequest(some_field="value") + assert stub.some_field == "value" + + def test_package_status_is_enum(self): + """PackageStatus should be an importable enum.""" + from adcp import PackageStatus + + assert hasattr(PackageStatus, "__members__") + + +class TestStatusTypeBackwardCompat: + """Status must resolve to delivery status, not invoice status.""" + + def test_status_is_delivery_status(self): + """Status from adcp.types should be the media buy delivery status.""" + from adcp.types import MediaBuyDeliveryStatus, Status + + assert Status is MediaBuyDeliveryStatus + + def test_status_has_delivery_values(self): + """Status should accept delivery status enum values.""" + from adcp.types import Status + + # Media buy delivery status values + for value in ["pending", "active", "paused", "completed", "failed"]: + status = Status(value) + assert status.value == value + + +class TestCatalogGroupBinding: + """CatalogFieldBinding1 should have a semantic alias.""" + + def test_catalog_group_binding_imports(self): + from adcp.types import CatalogGroupBinding + + assert CatalogGroupBinding is not None + + def test_catalog_group_binding_is_correct_type(self): + from adcp.types import CatalogGroupBinding + from adcp.types._generated import CatalogFieldBinding1 + + assert CatalogGroupBinding is CatalogFieldBinding1 + + def test_catalog_group_binding_has_correct_kind(self): + from adcp.types import CatalogGroupBinding + + assert "kind" in CatalogGroupBinding.__annotations__ + + +class TestAllBackwardCompatInAll: + """All backward-compat aliases must appear in __all__.""" + + def test_all_compat_aliases_in_types_all(self): + import adcp.types + + compat_aliases = [ + "CpmAuctionPricingOption", + "CpmFixedRatePricingOption", + "VcpmAuctionPricingOption", + "VcpmFixedRatePricingOption", + "PropertyIdActivationKey", + "PropertyTagActivationKey", + "PreviewCreativeFormatRequest", + "PreviewCreativeManifestRequest", + "PreviewCreativeStaticResponse", + "PreviewCreativeInteractiveResponse", + "ListAuthorizedPropertiesRequest", + "ListAuthorizedPropertiesResponse", + "PackageStatus", + ] + for alias in compat_aliases: + assert alias in adcp.types.__all__, f"{alias} missing from types.__all__" + + def test_all_compat_aliases_in_adcp_all(self): + import adcp + + compat_aliases = [ + "CpmAuctionPricingOption", + "CpmFixedRatePricingOption", + "VcpmAuctionPricingOption", + "VcpmFixedRatePricingOption", + "PropertyIdActivationKey", + "PropertyTagActivationKey", + "PreviewCreativeFormatRequest", + "PreviewCreativeManifestRequest", + "PreviewCreativeStaticResponse", + "PreviewCreativeInteractiveResponse", + "ListAuthorizedPropertiesRequest", + "ListAuthorizedPropertiesResponse", + "PackageStatus", + "DeliverTo", + "Measurement", + "Pricing", + ] + for alias in compat_aliases: + assert alias in adcp.__all__, f"{alias} missing from adcp.__all__" diff --git a/tests/test_catalog_types.py b/tests/test_catalog_types.py index 070e0b2c..35bd8f5a 100644 --- a/tests/test_catalog_types.py +++ b/tests/test_catalog_types.py @@ -155,12 +155,12 @@ def test_catalog_offering_type_inline(): def test_sync_catalogs_request_basic(): - """SyncCatalogsRequest accepts account_id with catalogs array.""" + """SyncCatalogsRequest accepts account with catalogs array.""" from adcp import SyncCatalogsRequest req = SyncCatalogsRequest.model_validate( { - "account_id": "acct_123", + "account": {"account_id": "acct_123"}, "catalogs": [ { "type": "product", @@ -172,7 +172,7 @@ def test_sync_catalogs_request_basic(): ], } ) - assert req.account_id == "acct_123" + assert req.account.root.account_id == "acct_123" assert req.catalogs is not None assert len(req.catalogs) == 1 @@ -181,8 +181,8 @@ def test_sync_catalogs_request_discovery_only(): """SyncCatalogsRequest accepts discovery-only mode (no catalogs).""" from adcp import SyncCatalogsRequest - req = SyncCatalogsRequest.model_validate({"account_id": "acct_123"}) - assert req.account_id == "acct_123" + req = SyncCatalogsRequest.model_validate({"account": {"account_id": "acct_123"}}) + assert req.account.root.account_id == "acct_123" assert req.catalogs is None @@ -319,84 +319,6 @@ def test_catalog_type_not_signal_catalog_type(): assert SignalCatalogType.marketplace.value == "marketplace" -def test_creative_catalogs_field_accepts_list(): - """Creative in list_creatives_response accepts catalogs as a non-empty list.""" - from adcp import Catalog - from adcp.types.generated_poc.media_buy.list_creatives_response import Creative as ListCreative - - creative = ListCreative.model_validate( - { - "creative_id": "c1", - "name": "Test Creative", - "created_date": "2026-01-01T00:00:00Z", - "updated_date": "2026-01-01T00:00:00Z", - "format_id": {"agent_url": "https://creative.adcontextprotocol.org", "id": "banner"}, - "status": "approved", - "catalogs": [{"type": "product", "catalog_id": "feed-1"}], - } - ) - assert creative.catalogs is not None - assert len(creative.catalogs) == 1 - assert isinstance(creative.catalogs[0], Catalog) - - -def test_creative_catalogs_field_rejects_empty_list(): - """Creative in list_creatives_response rejects empty catalogs list (min_length=1).""" - from adcp.types.generated_poc.media_buy.list_creatives_response import Creative as ListCreative - - with pytest.raises(ValidationError): - ListCreative.model_validate( - { - "creative_id": "c1", - "name": "Test Creative", - "created_date": "2026-01-01T00:00:00Z", - "updated_date": "2026-01-01T00:00:00Z", - "format_id": { - "agent_url": "https://creative.adcontextprotocol.org", - "id": "banner", - }, - "status": "approved", - "catalogs": [], - } - ) - - -def test_creative_asset_catalogs_field(): - """CreativeAsset accepts catalogs as a non-empty list.""" - from adcp import Catalog - from adcp.types import CreativeAsset - - asset = CreativeAsset.model_validate( - { - "creative_id": "c1", - "name": "Test Creative", - "format_id": {"agent_url": "https://creative.adcontextprotocol.org", "id": "banner"}, - "assets": {}, - "catalogs": [{"type": "product", "catalog_id": "feed-1"}], - } - ) - assert asset.catalogs is not None - assert len(asset.catalogs) == 1 - assert isinstance(asset.catalogs[0], Catalog) - - -def test_creative_manifest_catalogs_field(): - """CreativeManifest accepts catalogs as a non-empty list.""" - from adcp import Catalog - from adcp.types import CreativeManifest - - manifest = CreativeManifest.model_validate( - { - "format_id": {"agent_url": "https://creative.adcontextprotocol.org", "id": "banner"}, - "assets": {}, - "catalogs": [{"type": "offering", "items": [{"offering_id": "o1", "name": "Deal"}]}], - } - ) - assert manifest.catalogs is not None - assert len(manifest.catalogs) == 1 - assert isinstance(manifest.catalogs[0], Catalog) - - def test_universal_macro_catalog_values(): """UniversalMacro contains catalog-context macros added in schema sync.""" from adcp.types._generated import UniversalMacro diff --git a/tests/test_client.py b/tests/test_client.py index 1b2cd67e..c1eca872 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -107,11 +107,13 @@ async def test_get_products(): client.adapter, "_parse_response", return_value=mock_parsed_result ) as mock_parse, ): - request = GetProductsRequest(brief="test campaign") + request = GetProductsRequest.model_validate( + {"buying_mode": "brief", "brief": "test campaign"} + ) result = await client.get_products(request) # Verify adapter method was called - mock_get.assert_called_once_with({"brief": "test campaign"}) + mock_get.assert_called_once_with({"brief": "test campaign", "buying_mode": "brief"}) # Verify parsing was called with correct type mock_parse.assert_called_once_with(mock_raw_result, GetProductsResponse) # Verify final result @@ -176,12 +178,13 @@ async def test_all_client_methods(): @pytest.mark.parametrize( "method_name,request_class,request_data", [ - ("get_products", "GetProductsRequest", {}), + ("get_products", "GetProductsRequest", {"buying_mode": "wholesale"}), ("list_creative_formats", "ListCreativeFormatsRequest", {}), ( "sync_creatives", "SyncCreativesRequest", { + "account": {"account_id": "acct-1"}, "creatives": [ { "creative_id": "test", @@ -198,12 +201,12 @@ async def test_all_client_methods(): } }, } - ] + ], }, ), ("list_creatives", "ListCreativesRequest", {}), ("get_media_buy_delivery", "GetMediaBuyDeliveryRequest", {}), - ("get_media_buys", "GetMediaBuysRequest", {}), + ("get_media_buys", "GetMediaBuysRequest", {"account": {"account_id": "acct-1"}}), ( "get_signals", "GetSignalsRequest", @@ -211,7 +214,7 @@ async def test_all_client_methods(): "signal_spec": "test", "deliver_to": { "countries": ["US"], - "deployments": [{"type": "platform", "platform": "test"}], + "destinations": [{"type": "platform", "platform": "test"}], }, }, ), @@ -220,7 +223,7 @@ async def test_all_client_methods(): "ActivateSignalRequest", { "signal_agent_segment_id": "test", - "deployments": [{"type": "platform", "platform": "test"}], + "destinations": [{"type": "platform", "platform": "test"}], }, ), ( @@ -242,10 +245,9 @@ async def test_all_client_methods(): { "accounts": [ { - "account_id": "acct-1", - "brand_id": "test_brand", - "house": "example.com", - "name": "Test Account", + "billing": "operator", + "brand": {"domain": "example.com"}, + "operator": "test-operator", } ] }, @@ -267,7 +269,7 @@ async def test_all_client_methods(): ( "sync_event_sources", "SyncEventSourcesRequest", - {"account_id": "acct-1"}, + {"account": {"account_id": "acct-1"}}, ), ( "get_creative_delivery", @@ -355,12 +357,12 @@ async def test_multi_agent_parallel_execution(): client.agents["agent2"].adapter, "get_products", return_value=mock_result ) as mock2, ): - request = GetProductsRequest(brief="test") + request = GetProductsRequest.model_validate({"buying_mode": "wholesale"}) results = await client.get_products(request) # Verify both agents' get_products method was called - mock1.assert_called_once_with({"brief": "test"}) - mock2.assert_called_once_with({"brief": "test"}) + mock1.assert_called_once_with({"buying_mode": "wholesale"}) + mock2.assert_called_once_with({"buying_mode": "wholesale"}) # Verify results from both agents assert len(results) == 2 @@ -610,8 +612,12 @@ async def test_get_media_buys_parses_response(): with patch.object( client.adapter, "get_media_buys", return_value=mock_result ) as mock_adapter: - result = await client.get_media_buys(GetMediaBuysRequest(account_id="acct-1")) - mock_adapter.assert_called_once_with({"account_id": "acct-1", "include_snapshot": False}) + result = await client.get_media_buys( + GetMediaBuysRequest.model_validate({"account": {"account_id": "acct-1"}}) + ) + mock_adapter.assert_called_once_with( + {"account": {"account_id": "acct-1"}, "include_snapshot": False} + ) assert result.success is True assert isinstance(result.data, GetMediaBuysResponse) assert len(result.data.media_buys) == 1 @@ -691,8 +697,14 @@ async def test_get_media_buys_parses_snapshot_response(): with patch.object( client.adapter, "get_media_buys", return_value=mock_result ) as mock_adapter: - result = await client.get_media_buys(GetMediaBuysRequest(include_snapshot=True)) - mock_adapter.assert_called_once_with({"include_snapshot": True}) + result = await client.get_media_buys( + GetMediaBuysRequest.model_validate( + {"account": {"account_id": "acct-1"}, "include_snapshot": True} + ) + ) + mock_adapter.assert_called_once_with( + {"account": {"account_id": "acct-1"}, "include_snapshot": True} + ) assert result.success is True assert isinstance(result.data, GetMediaBuysResponse) diff --git a/tests/test_preview_html.py b/tests/test_preview_html.py index 5698fd19..bc9a3430 100644 --- a/tests/test_preview_html.py +++ b/tests/test_preview_html.py @@ -309,7 +309,9 @@ async def test_get_products_with_preview_urls(): "_parse_response", return_value=mock_preview_parsed_result, ): - request = GetProductsRequest(brief="test campaign") + request = GetProductsRequest.model_validate( + {"buying_mode": "brief", "brief": "test campaign"} + ) result = await client.get_products( request, fetch_previews=True, creative_agent_client=creative_client ) @@ -336,7 +338,9 @@ async def test_get_products_without_creative_client_raises_error(): client = ADCPClient(config) with pytest.raises(ValueError, match="creative_agent_client is required"): - request = GetProductsRequest(brief="test campaign") + request = GetProductsRequest.model_validate( + {"buying_mode": "brief", "brief": "test campaign"} + ) await client.get_products(request, fetch_previews=True) diff --git a/tests/test_response_str.py b/tests/test_response_str.py index b6411c1d..5c328d2a 100644 --- a/tests/test_response_str.py +++ b/tests/test_response_str.py @@ -310,16 +310,18 @@ class TestNonResponseTypeMessage: def test_request_type_returns_generic_message(self): """Request types return generic message with class name.""" - from adcp.types import GetProductsRequest + from adcp.types import ListCreativesRequest - request = GetProductsRequest(brief="Test brief") - assert request.model_summary() == "GetProductsRequest response" + request = ListCreativesRequest() + assert request.model_summary() == "ListCreativesRequest response" def test_str_returns_pydantic_default(self): """str() returns Pydantic's default representation for inspection.""" - from adcp.types import GetProductsRequest + from adcp.types import ListCreativesRequest - request = GetProductsRequest(brief="Test brief") + request = ListCreativesRequest() result = str(request) - # Should be Pydantic's default format, not a custom message - assert "GetProductsRequest" in result or "brief=" in result + # Should be Pydantic's default field=value format with actual field names + assert "context=" in result + assert "fields=" in result + assert "filters=" in result diff --git a/tests/test_simple_api.py b/tests/test_simple_api.py index fd7b2a60..a310f797 100644 --- a/tests/test_simple_api.py +++ b/tests/test_simple_api.py @@ -33,7 +33,9 @@ async def test_get_products_simple_api(): # Mock the client's get_products method with patch.object(test_agent, "get_products", new=AsyncMock(return_value=mock_result)): # Call simplified API with kwargs - result = await test_agent.simple.get_products(brief="Coffee subscription service") + result = await test_agent.simple.get_products( + buying_mode="brief", brief="Coffee subscription service" + ) # Verify it returns unwrapped data assert isinstance(result, GetProductsResponse) @@ -43,7 +45,7 @@ async def test_get_products_simple_api(): # Verify the underlying call was made correctly test_agent.get_products.assert_called_once() call_args = test_agent.get_products.call_args[0][0] - assert call_args.brief == "Coffee subscription service" + assert call_args.root.brief == "Coffee subscription service" @pytest.mark.asyncio @@ -59,7 +61,7 @@ async def test_get_products_simple_api_failure(): with patch.object(test_agent, "get_products", new=AsyncMock(return_value=mock_result)): # Should raise ADCPSimpleAPIError on failure with pytest.raises(ADCPSimpleAPIError, match="get_products failed"): - await test_agent.simple.get_products(brief="Test") + await test_agent.simple.get_products(buying_mode="brief", brief="Test") def test_simple_api_has_no_sync_methods(): diff --git a/tests/test_type_aliases.py b/tests/test_type_aliases.py index 381b9a1b..5d9a36d8 100644 --- a/tests/test_type_aliases.py +++ b/tests/test_type_aliases.py @@ -144,8 +144,9 @@ def test_all_response_aliases_exported(): def test_all_request_aliases_exported(): """Test that all expected request type aliases are exported.""" expected_aliases = [ - "PreviewCreativeFormatRequest", - "PreviewCreativeManifestRequest", + "PreviewCreativeSingleRequest", + "PreviewCreativeBatchRequest", + "PreviewCreativeVariantRequest", "UpdateMediaBuyPackagesRequest", "UpdateMediaBuyPropertiesRequest", ] @@ -158,28 +159,24 @@ def test_all_request_aliases_exported(): def test_all_activation_key_aliases_exported(): - """Test that activation key types are available. - - Note: The activation key schema changed in the latest ADCP schemas. - Previously it had property_id/property_tag variants (PropertyIdActivationKey, - PropertyTagActivationKey). Now it uses segment_id/key_value variants. - Direct activation key types are available from the generated types. - """ - # Activation key types are now segment_id and key_value based - # Import directly from generated types, not aliases - from adcp.types._generated import ActivationKey, ActivationKey1, ActivationKey2 + """Test that activation key aliases are exported from the public API.""" + import adcp.types.aliases as aliases_module - # Basic sanity check that the types exist - assert ActivationKey is not None - assert ActivationKey1 is not None # segment_id variant - assert ActivationKey2 is not None # key_value variant + expected_aliases = [ + "SegmentIdActivationKey", + "KeyValueActivationKey", + ] + for alias in expected_aliases: + assert hasattr(aliases_module, alias), f"Missing alias: {alias}" + assert alias in aliases_module.__all__, f"Alias not in __all__: {alias}" def test_all_preview_render_aliases_exported(): """Test that all preview render aliases are exported.""" expected_aliases = [ - "PreviewCreativeStaticResponse", - "PreviewCreativeInteractiveResponse", + "PreviewCreativeSingleResponse", + "PreviewCreativeBatchResponse", + "PreviewCreativeVariantResponse", # Semantic aliases based on output_format discriminator "UrlPreviewRender", "HtmlPreviewRender", @@ -606,6 +603,7 @@ def test_pricing_option_union_contains_all_variants(): from typing import get_args from adcp import ( + CpaPricingOption, CpcPricingOption, CpcvPricingOption, CpmPricingOption, @@ -613,6 +611,7 @@ def test_pricing_option_union_contains_all_variants(): CpvPricingOption, FlatRatePricingOption, PricingOption, + TimeBasedPricingOption, VcpmPricingOption, ) @@ -627,7 +626,9 @@ def test_pricing_option_union_contains_all_variants(): CpcvPricingOption, CpvPricingOption, CppPricingOption, + CpaPricingOption, FlatRatePricingOption, + TimeBasedPricingOption, } assert union_args == expected_variants diff --git a/tests/test_type_coercion.py b/tests/test_type_coercion.py index e9397e9f..d500aff2 100644 --- a/tests/test_type_coercion.py +++ b/tests/test_type_coercion.py @@ -23,7 +23,8 @@ from adcp.types.generated_poc.core.ext import ExtensionObject from adcp.types.generated_poc.enums.creative_sort_field import CreativeSortField from adcp.types.generated_poc.enums.sort_direction import SortDirection -from adcp.types.generated_poc.media_buy.list_creatives_request import FieldModel, Sort +from adcp.types.generated_poc.media_buy.list_creatives_request import Field1 as FieldModel +from adcp.types.generated_poc.media_buy.list_creatives_request import Sort class TestEnumStringCoercion: @@ -120,9 +121,11 @@ def test_ext_accepts_dict(self): def test_get_products_request_context_accepts_dict(self): """GetProductsRequest.context accepts dict.""" - req = GetProductsRequest(context={"key": "value"}) - assert isinstance(req.context, ContextObject) - assert req.context.key == "value" + req = GetProductsRequest.model_validate( + {"buying_mode": "wholesale", "context": {"key": "value"}} + ) + assert isinstance(req.root.context, ContextObject) + assert req.root.context.key == "value" class TestFieldModelStringCoercion: @@ -319,7 +322,7 @@ class ExtendedPackage(PackageRequest): # No cast() needed! request = CreateMediaBuyRequest( - account_id="acct-1", + account={"account_id": "acct-1"}, brand={"domain": "example.com"}, buyer_ref="buyer-ref", start_time=datetime.now(timezone.utc),