adcp 2.17.0__py3-none-any.whl → 2.19.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. adcp/ADCP_VERSION +1 -1
  2. adcp/__init__.py +22 -1
  3. adcp/__main__.py +72 -0
  4. adcp/protocols/mcp.py +3 -1
  5. adcp/types/_ergonomic.py +0 -4
  6. adcp/types/_generated.py +91 -4
  7. adcp/types/generated_poc/adagents.py +239 -149
  8. adcp/types/generated_poc/core/activation_key.py +9 -9
  9. adcp/types/generated_poc/core/assets/audio_asset.py +6 -6
  10. adcp/types/generated_poc/core/assets/css_asset.py +3 -3
  11. adcp/types/generated_poc/core/assets/daast_asset.py +19 -19
  12. adcp/types/generated_poc/core/assets/html_asset.py +3 -3
  13. adcp/types/generated_poc/core/assets/image_asset.py +7 -7
  14. adcp/types/generated_poc/core/assets/javascript_asset.py +4 -4
  15. adcp/types/generated_poc/core/assets/text_asset.py +3 -3
  16. adcp/types/generated_poc/core/assets/url_asset.py +4 -4
  17. adcp/types/generated_poc/core/assets/vast_asset.py +19 -19
  18. adcp/types/generated_poc/core/assets/video_asset.py +8 -8
  19. adcp/types/generated_poc/core/assets/webhook_asset.py +10 -10
  20. adcp/types/generated_poc/core/async_response_data.py +2 -2
  21. adcp/types/generated_poc/core/brand_manifest.py +56 -56
  22. adcp/types/generated_poc/core/brand_manifest_ref.py +9 -9
  23. adcp/types/generated_poc/core/context.py +2 -3
  24. adcp/types/generated_poc/core/creative_asset.py +14 -14
  25. adcp/types/generated_poc/core/creative_assignment.py +4 -4
  26. adcp/types/generated_poc/core/creative_filters.py +20 -20
  27. adcp/types/generated_poc/core/creative_manifest.py +3 -3
  28. adcp/types/generated_poc/core/creative_policy.py +5 -5
  29. adcp/types/generated_poc/core/delivery_metrics.py +33 -33
  30. adcp/types/generated_poc/core/deployment.py +21 -21
  31. adcp/types/generated_poc/core/destination.py +12 -12
  32. adcp/types/generated_poc/core/error.py +9 -9
  33. adcp/types/generated_poc/core/ext.py +2 -3
  34. adcp/types/generated_poc/core/format.py +139 -51
  35. adcp/types/generated_poc/core/format_id.py +6 -6
  36. adcp/types/generated_poc/core/frequency_cap.py +3 -3
  37. adcp/types/generated_poc/core/identifier.py +27 -0
  38. adcp/types/generated_poc/core/mcp_webhook_payload.py +10 -10
  39. adcp/types/generated_poc/core/measurement.py +9 -9
  40. adcp/types/generated_poc/core/media_buy.py +8 -8
  41. adcp/types/generated_poc/core/package.py +9 -9
  42. adcp/types/generated_poc/core/performance_feedback.py +19 -19
  43. adcp/types/generated_poc/core/placement.py +5 -5
  44. adcp/types/generated_poc/core/pricing_option.py +2 -2
  45. adcp/types/generated_poc/core/product.py +21 -21
  46. adcp/types/generated_poc/core/product_filters.py +19 -19
  47. adcp/types/generated_poc/core/promoted_offerings.py +21 -21
  48. adcp/types/generated_poc/core/promoted_products.py +3 -3
  49. adcp/types/generated_poc/core/property.py +10 -10
  50. adcp/types/generated_poc/core/property_id.py +4 -4
  51. adcp/types/generated_poc/core/property_list_ref.py +26 -0
  52. adcp/types/generated_poc/core/property_tag.py +4 -4
  53. adcp/types/generated_poc/core/protocol_envelope.py +9 -9
  54. adcp/types/generated_poc/core/publisher_property_selector.py +14 -14
  55. adcp/types/generated_poc/core/push_notification_config.py +5 -5
  56. adcp/types/generated_poc/core/reporting_capabilities.py +9 -9
  57. adcp/types/generated_poc/core/response.py +5 -5
  58. adcp/types/generated_poc/core/signal_filters.py +6 -6
  59. adcp/types/generated_poc/core/start_timing.py +5 -5
  60. adcp/types/generated_poc/core/sub_asset.py +15 -15
  61. adcp/types/generated_poc/core/targeting.py +9 -9
  62. adcp/types/generated_poc/creative/list_creative_formats_request.py +21 -21
  63. adcp/types/generated_poc/creative/list_creative_formats_response.py +6 -6
  64. adcp/types/generated_poc/creative/preview_creative_request.py +26 -26
  65. adcp/types/generated_poc/creative/preview_creative_response.py +31 -30
  66. adcp/types/generated_poc/creative/preview_render.py +26 -26
  67. adcp/types/generated_poc/enums/adcp_domain.py +5 -3
  68. adcp/types/generated_poc/enums/asset_content_type.py +13 -13
  69. adcp/types/generated_poc/enums/auth_scheme.py +2 -2
  70. adcp/types/generated_poc/enums/available_metric.py +9 -9
  71. adcp/types/generated_poc/enums/channels.py +9 -9
  72. adcp/types/generated_poc/enums/co_branding_requirement.py +3 -3
  73. adcp/types/generated_poc/enums/creative_action.py +5 -5
  74. adcp/types/generated_poc/enums/creative_agent_capability.py +4 -4
  75. adcp/types/generated_poc/enums/creative_sort_field.py +6 -6
  76. adcp/types/generated_poc/enums/creative_status.py +4 -4
  77. adcp/types/generated_poc/enums/daast_tracking_event.py +11 -11
  78. adcp/types/generated_poc/enums/daast_version.py +2 -2
  79. adcp/types/generated_poc/enums/delivery_type.py +2 -2
  80. adcp/types/generated_poc/enums/dimension_unit.py +4 -4
  81. adcp/types/generated_poc/enums/feed_format.py +3 -3
  82. adcp/types/generated_poc/enums/feedback_source.py +4 -4
  83. adcp/types/generated_poc/enums/format_category.py +7 -7
  84. adcp/types/generated_poc/enums/format_id_parameter.py +2 -2
  85. adcp/types/generated_poc/enums/frequency_cap_scope.py +3 -3
  86. adcp/types/generated_poc/enums/history_entry_type.py +2 -2
  87. adcp/types/generated_poc/enums/http_method.py +2 -2
  88. adcp/types/generated_poc/enums/identifier_types.py +19 -19
  89. adcp/types/generated_poc/enums/javascript_module_type.py +3 -3
  90. adcp/types/generated_poc/enums/landing_page_requirement.py +3 -3
  91. adcp/types/generated_poc/enums/markdown_flavor.py +2 -2
  92. adcp/types/generated_poc/enums/media_buy_status.py +4 -4
  93. adcp/types/generated_poc/enums/metric_type.py +8 -8
  94. adcp/types/generated_poc/enums/notification_type.py +4 -4
  95. adcp/types/generated_poc/enums/pacing.py +3 -3
  96. adcp/types/generated_poc/enums/preview_output_format.py +2 -2
  97. adcp/types/generated_poc/enums/pricing_model.py +7 -7
  98. adcp/types/generated_poc/enums/property_type.py +7 -7
  99. adcp/types/generated_poc/enums/publisher_identifier_types.py +5 -5
  100. adcp/types/generated_poc/enums/reporting_frequency.py +3 -3
  101. adcp/types/generated_poc/enums/signal_catalog_type.py +3 -3
  102. adcp/types/generated_poc/enums/sort_direction.py +2 -2
  103. adcp/types/generated_poc/enums/standard_format_ids.py +35 -35
  104. adcp/types/generated_poc/enums/task_status.py +9 -9
  105. adcp/types/generated_poc/enums/task_type.py +12 -6
  106. adcp/types/generated_poc/enums/update_frequency.py +4 -4
  107. adcp/types/generated_poc/enums/url_asset_type.py +3 -3
  108. adcp/types/generated_poc/enums/validation_mode.py +2 -2
  109. adcp/types/generated_poc/enums/vast_tracking_event.py +16 -16
  110. adcp/types/generated_poc/enums/vast_version.py +5 -5
  111. adcp/types/generated_poc/enums/webhook_response_type.py +4 -4
  112. adcp/types/generated_poc/enums/webhook_security_method.py +3 -3
  113. adcp/types/generated_poc/extensions/__init__.py +3 -0
  114. adcp/types/generated_poc/extensions/extension_meta.py +50 -0
  115. adcp/types/generated_poc/media_buy/build_creative_request.py +5 -5
  116. adcp/types/generated_poc/media_buy/build_creative_response.py +7 -7
  117. adcp/types/generated_poc/media_buy/create_media_buy_async_response_input_required.py +6 -6
  118. adcp/types/generated_poc/media_buy/create_media_buy_async_response_submitted.py +2 -2
  119. adcp/types/generated_poc/media_buy/create_media_buy_async_response_working.py +6 -6
  120. adcp/types/generated_poc/media_buy/create_media_buy_request.py +64 -23
  121. adcp/types/generated_poc/media_buy/create_media_buy_response.py +8 -8
  122. adcp/types/generated_poc/media_buy/get_media_buy_delivery_request.py +9 -9
  123. adcp/types/generated_poc/media_buy/get_media_buy_delivery_response.py +52 -52
  124. adcp/types/generated_poc/media_buy/get_products_async_response_input_required.py +7 -7
  125. adcp/types/generated_poc/media_buy/get_products_async_response_submitted.py +3 -3
  126. adcp/types/generated_poc/media_buy/get_products_async_response_working.py +5 -5
  127. adcp/types/generated_poc/media_buy/get_products_request.py +11 -5
  128. adcp/types/generated_poc/media_buy/get_products_response.py +10 -4
  129. adcp/types/generated_poc/media_buy/list_authorized_properties_request.py +4 -4
  130. adcp/types/generated_poc/media_buy/list_authorized_properties_response.py +8 -8
  131. adcp/types/generated_poc/media_buy/list_creative_formats_request.py +10 -10
  132. adcp/types/generated_poc/media_buy/list_creative_formats_response.py +6 -6
  133. adcp/types/generated_poc/media_buy/list_creatives_request.py +24 -24
  134. adcp/types/generated_poc/media_buy/list_creatives_response.py +53 -53
  135. adcp/types/generated_poc/media_buy/package_request.py +16 -7
  136. adcp/types/generated_poc/media_buy/provide_performance_feedback_request.py +20 -20
  137. adcp/types/generated_poc/media_buy/provide_performance_feedback_response.py +7 -7
  138. adcp/types/generated_poc/media_buy/sync_creatives_async_response_input_required.py +6 -6
  139. adcp/types/generated_poc/media_buy/sync_creatives_async_response_submitted.py +2 -2
  140. adcp/types/generated_poc/media_buy/sync_creatives_async_response_working.py +8 -8
  141. adcp/types/generated_poc/media_buy/sync_creatives_request.py +8 -8
  142. adcp/types/generated_poc/media_buy/sync_creatives_response.py +16 -16
  143. adcp/types/generated_poc/media_buy/update_media_buy_async_response_input_required.py +5 -5
  144. adcp/types/generated_poc/media_buy/update_media_buy_async_response_submitted.py +2 -2
  145. adcp/types/generated_poc/media_buy/update_media_buy_async_response_working.py +6 -6
  146. adcp/types/generated_poc/media_buy/update_media_buy_request.py +37 -29
  147. adcp/types/generated_poc/media_buy/update_media_buy_response.py +8 -8
  148. adcp/types/generated_poc/pricing_options/cpc_option.py +9 -9
  149. adcp/types/generated_poc/pricing_options/cpcv_option.py +9 -9
  150. adcp/types/generated_poc/pricing_options/cpm_auction_option.py +14 -14
  151. adcp/types/generated_poc/pricing_options/cpm_fixed_option.py +9 -9
  152. adcp/types/generated_poc/pricing_options/cpp_option.py +14 -14
  153. adcp/types/generated_poc/pricing_options/cpv_option.py +13 -13
  154. adcp/types/generated_poc/pricing_options/flat_rate_option.py +16 -16
  155. adcp/types/generated_poc/pricing_options/vcpm_auction_option.py +14 -14
  156. adcp/types/generated_poc/pricing_options/vcpm_fixed_option.py +9 -9
  157. adcp/types/generated_poc/property/__init__.py +3 -0
  158. adcp/types/generated_poc/property/base_property_source.py +86 -0
  159. adcp/types/generated_poc/property/create_property_list_request.py +43 -0
  160. adcp/types/generated_poc/property/create_property_list_response.py +27 -0
  161. adcp/types/generated_poc/property/delete_property_list_request.py +22 -0
  162. adcp/types/generated_poc/property/delete_property_list_response.py +21 -0
  163. adcp/types/generated_poc/property/feature_requirement.py +42 -0
  164. adcp/types/generated_poc/property/get_property_list_request.py +34 -0
  165. adcp/types/generated_poc/property/get_property_list_response.py +61 -0
  166. adcp/types/generated_poc/property/list_property_features_request.py +25 -0
  167. adcp/types/generated_poc/property/list_property_features_response.py +24 -0
  168. adcp/types/generated_poc/property/list_property_lists_request.py +29 -0
  169. adcp/types/generated_poc/property/list_property_lists_response.py +39 -0
  170. adcp/types/generated_poc/property/property_error.py +33 -0
  171. adcp/types/generated_poc/property/property_feature.py +22 -0
  172. adcp/types/generated_poc/property/property_feature_definition.py +80 -0
  173. adcp/types/generated_poc/property/property_list.py +62 -0
  174. adcp/types/generated_poc/property/property_list_changed_webhook.py +51 -0
  175. adcp/types/generated_poc/property/property_list_filters.py +47 -0
  176. adcp/types/generated_poc/property/update_property_list_request.py +46 -0
  177. adcp/types/generated_poc/property/update_property_list_response.py +21 -0
  178. adcp/types/generated_poc/protocols/adcp_extension.py +26 -10
  179. adcp/types/generated_poc/signals/activate_signal_request.py +4 -4
  180. adcp/types/generated_poc/signals/activate_signal_response.py +7 -7
  181. adcp/types/generated_poc/signals/get_signals_request.py +9 -9
  182. adcp/types/generated_poc/signals/get_signals_response.py +16 -16
  183. adcp/utils/__init__.py +24 -1
  184. adcp/utils/format_assets.py +224 -0
  185. adcp/utils/preview_cache.py +29 -7
  186. {adcp-2.17.0.dist-info → adcp-2.19.0.dist-info}/METADATA +1 -1
  187. adcp-2.19.0.dist-info/RECORD +220 -0
  188. adcp-2.17.0.dist-info/RECORD +0 -194
  189. {adcp-2.17.0.dist-info → adcp-2.19.0.dist-info}/WHEEL +0 -0
  190. {adcp-2.17.0.dist-info → adcp-2.19.0.dist-info}/entry_points.txt +0 -0
  191. {adcp-2.17.0.dist-info → adcp-2.19.0.dist-info}/licenses/LICENSE +0 -0
  192. {adcp-2.17.0.dist-info → adcp-2.19.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: adagents.json
3
- # timestamp: 2025-12-18T20:00:24+00:00
3
+ # timestamp: 2026-01-14T17:08:13+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -14,39 +14,39 @@ from .core import property, property_id, property_tag, publisher_property_select
14
14
 
15
15
  class AuthorizedSalesAgents1(AdCPBaseModel):
16
16
  model_config = ConfigDict(
17
- extra='forbid',
17
+ extra="allow",
18
18
  )
19
19
  field_schema: Annotated[
20
20
  str | None,
21
- Field(alias='$schema', description='JSON Schema identifier for this adagents.json file'),
21
+ Field(alias="$schema", description="JSON Schema identifier for this adagents.json file"),
22
22
  ] = None
23
23
  authoritative_location: Annotated[
24
24
  AnyUrl,
25
25
  Field(
26
- description='HTTPS URL of the authoritative adagents.json file. When present, this file is a reference and the authoritative location contains the actual agent authorization data.'
26
+ description="HTTPS URL of the authoritative adagents.json file. When present, this file is a reference and the authoritative location contains the actual agent authorization data."
27
27
  ),
28
28
  ]
29
29
  last_updated: Annotated[
30
30
  AwareDatetime | None,
31
- Field(description='ISO 8601 timestamp indicating when this reference was last updated'),
31
+ Field(description="ISO 8601 timestamp indicating when this reference was last updated"),
32
32
  ] = None
33
33
 
34
34
 
35
35
  class Contact(AdCPBaseModel):
36
36
  model_config = ConfigDict(
37
- extra='forbid',
37
+ extra="allow",
38
38
  )
39
39
  domain: Annotated[
40
40
  str | None,
41
41
  Field(
42
- description='Primary domain of the entity managing this file',
43
- pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$',
42
+ description="Primary domain of the entity managing this file",
43
+ pattern="^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$",
44
44
  ),
45
45
  ] = None
46
46
  email: Annotated[
47
47
  EmailStr | None,
48
48
  Field(
49
- description='Contact email for questions or issues with this authorization file',
49
+ description="Contact email for questions or issues with this authorization file",
50
50
  max_length=255,
51
51
  min_length=1,
52
52
  ),
@@ -62,7 +62,7 @@ class Contact(AdCPBaseModel):
62
62
  seller_id: Annotated[
63
63
  str | None,
64
64
  Field(
65
- description='Seller ID from IAB Tech Lab sellers.json (if applicable)',
65
+ description="Seller ID from IAB Tech Lab sellers.json (if applicable)",
66
66
  max_length=255,
67
67
  min_length=1,
68
68
  ),
@@ -70,33 +70,59 @@ class Contact(AdCPBaseModel):
70
70
  tag_id: Annotated[
71
71
  str | None,
72
72
  Field(
73
- description='TAG Certified Against Fraud ID for verification (if applicable)',
73
+ description="TAG Certified Against Fraud ID for verification (if applicable)",
74
74
  max_length=100,
75
75
  min_length=1,
76
76
  ),
77
77
  ] = None
78
78
 
79
79
 
80
+ class PropertyFeature(AdCPBaseModel):
81
+ model_config = ConfigDict(
82
+ extra="allow",
83
+ )
84
+ features: Annotated[
85
+ list[str],
86
+ Field(
87
+ description="Feature IDs this agent provides (e.g., 'carbon_score', 'tag_certified_against_fraud'). Use list_property_features on the agent for full definitions.",
88
+ min_length=1,
89
+ ),
90
+ ]
91
+ name: Annotated[
92
+ str,
93
+ Field(
94
+ description="Human-readable name of the vendor/agent (e.g., 'Scope3', 'TAG', 'OneTrust')"
95
+ ),
96
+ ]
97
+ publisher_id: Annotated[
98
+ str | None, Field(description="Optional publisher identifier at this agent (for lookup)")
99
+ ] = None
100
+ url: Annotated[
101
+ AnyUrl,
102
+ Field(description="The agent's API endpoint URL (must implement get_property_features)"),
103
+ ]
104
+
105
+
80
106
  class Tags(AdCPBaseModel):
81
107
  model_config = ConfigDict(
82
- extra='forbid',
108
+ extra="allow",
83
109
  )
84
- description: Annotated[str, Field(description='Description of what this tag represents')]
85
- name: Annotated[str, Field(description='Human-readable name for this tag')]
110
+ description: Annotated[str, Field(description="Description of what this tag represents")]
111
+ name: Annotated[str, Field(description="Human-readable name for this tag")]
86
112
 
87
113
 
88
114
  class AuthorizedAgents(AdCPBaseModel):
89
115
  model_config = ConfigDict(
90
- extra='forbid',
116
+ extra="allow",
91
117
  )
92
118
  authorization_type: Annotated[
93
- Literal['property_ids'],
94
- Field(description='Discriminator indicating authorization by specific property IDs'),
119
+ Literal["property_ids"],
120
+ Field(description="Discriminator indicating authorization by specific property IDs"),
95
121
  ]
96
122
  authorized_for: Annotated[
97
123
  str,
98
124
  Field(
99
- description='Human-readable description of what this agent is authorized to sell',
125
+ description="Human-readable description of what this agent is authorized to sell",
100
126
  max_length=500,
101
127
  min_length=1,
102
128
  ),
@@ -104,7 +130,7 @@ class AuthorizedAgents(AdCPBaseModel):
104
130
  property_ids: Annotated[
105
131
  list[property_id.PropertyId],
106
132
  Field(
107
- description='Property IDs this agent is authorized for. Resolved against the top-level properties array in this file',
133
+ description="Property IDs this agent is authorized for. Resolved against the top-level properties array in this file",
108
134
  min_length=1,
109
135
  ),
110
136
  ]
@@ -113,16 +139,16 @@ class AuthorizedAgents(AdCPBaseModel):
113
139
 
114
140
  class AuthorizedAgents1(AdCPBaseModel):
115
141
  model_config = ConfigDict(
116
- extra='forbid',
142
+ extra="allow",
117
143
  )
118
144
  authorization_type: Annotated[
119
- Literal['property_tags'],
120
- Field(description='Discriminator indicating authorization by property tags'),
145
+ Literal["property_tags"],
146
+ Field(description="Discriminator indicating authorization by property tags"),
121
147
  ]
122
148
  authorized_for: Annotated[
123
149
  str,
124
150
  Field(
125
- description='Human-readable description of what this agent is authorized to sell',
151
+ description="Human-readable description of what this agent is authorized to sell",
126
152
  max_length=500,
127
153
  min_length=1,
128
154
  ),
@@ -130,7 +156,7 @@ class AuthorizedAgents1(AdCPBaseModel):
130
156
  property_tags: Annotated[
131
157
  list[property_tag.PropertyTag],
132
158
  Field(
133
- description='Tags identifying which properties this agent is authorized for. Resolved against the top-level properties array in this file using tag matching',
159
+ description="Tags identifying which properties this agent is authorized for. Resolved against the top-level properties array in this file using tag matching",
134
160
  min_length=1,
135
161
  ),
136
162
  ]
@@ -139,18 +165,18 @@ class AuthorizedAgents1(AdCPBaseModel):
139
165
 
140
166
  class AuthorizedAgents3(AdCPBaseModel):
141
167
  model_config = ConfigDict(
142
- extra='forbid',
168
+ extra="allow",
143
169
  )
144
170
  authorization_type: Annotated[
145
- Literal['publisher_properties'],
171
+ Literal["publisher_properties"],
146
172
  Field(
147
- description='Discriminator indicating authorization for properties from other publisher domains'
173
+ description="Discriminator indicating authorization for properties from other publisher domains"
148
174
  ),
149
175
  ]
150
176
  authorized_for: Annotated[
151
177
  str,
152
178
  Field(
153
- description='Human-readable description of what this agent is authorized to sell',
179
+ description="Human-readable description of what this agent is authorized to sell",
154
180
  max_length=500,
155
181
  min_length=1,
156
182
  ),
@@ -158,7 +184,7 @@ class AuthorizedAgents3(AdCPBaseModel):
158
184
  publisher_properties: Annotated[
159
185
  list[publisher_property_selector.PublisherPropertySelector],
160
186
  Field(
161
- description='Properties from other publisher domains this agent is authorized for. Each entry specifies a publisher domain and which of their properties this agent can sell',
187
+ description="Properties from other publisher domains this agent is authorized for. Each entry specifies a publisher domain and which of their properties this agent can sell",
162
188
  min_length=1,
163
189
  ),
164
190
  ]
@@ -167,16 +193,16 @@ class AuthorizedAgents3(AdCPBaseModel):
167
193
 
168
194
  class AuthorizedAgents2(AdCPBaseModel):
169
195
  model_config = ConfigDict(
170
- extra='forbid',
196
+ extra="allow",
171
197
  )
172
198
  authorization_type: Annotated[
173
- Literal['inline_properties'],
174
- Field(description='Discriminator indicating authorization by inline property definitions'),
199
+ Literal["inline_properties"],
200
+ Field(description="Discriminator indicating authorization by inline property definitions"),
175
201
  ]
176
202
  authorized_for: Annotated[
177
203
  str,
178
204
  Field(
179
- description='Human-readable description of what this agent is authorized to sell',
205
+ description="Human-readable description of what this agent is authorized to sell",
180
206
  max_length=500,
181
207
  min_length=1,
182
208
  ),
@@ -184,7 +210,7 @@ class AuthorizedAgents2(AdCPBaseModel):
184
210
  properties: Annotated[
185
211
  list[property.Property],
186
212
  Field(
187
- description='Specific properties this agent is authorized for (alternative to property_ids/property_tags)',
213
+ description="Specific properties this agent is authorized for (alternative to property_ids/property_tags)",
188
214
  min_length=1,
189
215
  ),
190
216
  ]
@@ -193,40 +219,46 @@ class AuthorizedAgents2(AdCPBaseModel):
193
219
 
194
220
  class AuthorizedSalesAgents2(AdCPBaseModel):
195
221
  model_config = ConfigDict(
196
- extra='forbid',
222
+ extra="allow",
197
223
  )
198
224
  field_schema: Annotated[
199
225
  str | None,
200
- Field(alias='$schema', description='JSON Schema identifier for this adagents.json file'),
226
+ Field(alias="$schema", description="JSON Schema identifier for this adagents.json file"),
201
227
  ] = None
202
228
  authorized_agents: Annotated[
203
229
  list[AuthorizedAgents | AuthorizedAgents1 | AuthorizedAgents2 | AuthorizedAgents3],
204
230
  Field(
205
- description='Array of sales agents authorized to sell inventory for properties in this file',
231
+ description="Array of sales agents authorized to sell inventory for properties in this file",
206
232
  min_length=1,
207
233
  ),
208
234
  ]
209
235
  contact: Annotated[
210
236
  Contact | None,
211
237
  Field(
212
- description='Contact information for the entity managing this adagents.json file (may be publisher or third-party operator)'
238
+ description="Contact information for the entity managing this adagents.json file (may be publisher or third-party operator)"
213
239
  ),
214
240
  ] = None
215
241
  last_updated: Annotated[
216
242
  AwareDatetime | None,
217
- Field(description='ISO 8601 timestamp indicating when this file was last updated'),
243
+ Field(description="ISO 8601 timestamp indicating when this file was last updated"),
218
244
  ] = None
219
245
  properties: Annotated[
220
246
  list[property.Property] | None,
221
247
  Field(
222
- description='Array of all properties covered by this adagents.json file. Defines the canonical property list that authorized agents reference.',
248
+ description="Array of all properties covered by this adagents.json file. Defines the canonical property list that authorized agents reference.",
223
249
  min_length=1,
224
250
  ),
225
251
  ] = None
252
+ property_features: Annotated[
253
+ list[PropertyFeature] | None,
254
+ Field(
255
+ description="[AdCP 3.0] Optional list of agents that provide property feature data (certifications, scores, compliance status). Used for discovery - actual data comes from querying the agent's get_property_features task."
256
+ ),
257
+ ] = None
226
258
  tags: Annotated[
227
259
  dict[str, Tags] | None,
228
260
  Field(
229
- description='Metadata for each tag referenced by properties. Provides human-readable context for property tag values.'
261
+ description="Metadata for each tag referenced by properties. Provides human-readable context for property tag values."
230
262
  ),
231
263
  ] = None
232
264
 
@@ -235,173 +267,231 @@ class AuthorizedSalesAgents(RootModel[AuthorizedSalesAgents1 | AuthorizedSalesAg
235
267
  root: Annotated[
236
268
  AuthorizedSalesAgents1 | AuthorizedSalesAgents2,
237
269
  Field(
238
- description='Declaration of authorized sales agents for advertising inventory. Hosted at /.well-known/adagents.json on publisher domains. Can either contain the full structure inline or reference an authoritative URL.',
270
+ description="Declaration of authorized sales agents for advertising inventory. Hosted at /.well-known/adagents.json on publisher domains. Can either contain the full structure inline or reference an authoritative URL.",
239
271
  examples=[
240
272
  {
241
- '$schema': '/schemas/latest/adagents.json',
242
- 'authoritative_location': 'https://cdn.example.com/adagents/v2/adagents.json',
243
- 'last_updated': '2025-01-15T10:00:00Z',
273
+ "$schema": "/schemas/2.6.0/adagents.json",
274
+ "authoritative_location": "https://cdn.example.com/adagents/v2/adagents.json",
275
+ "last_updated": "2025-01-15T10:00:00Z",
244
276
  },
245
277
  {
246
- '$schema': '/schemas/latest/adagents.json',
247
- 'authorized_agents': [
278
+ "$schema": "/schemas/2.6.0/adagents.json",
279
+ "authorized_agents": [
248
280
  {
249
- 'authorization_type': 'property_tags',
250
- 'authorized_for': 'Official sales agent',
251
- 'property_tags': ['all'],
252
- 'url': 'https://agent.example.com',
281
+ "authorization_type": "property_tags",
282
+ "authorized_for": "Official sales agent",
283
+ "property_tags": ["all"],
284
+ "url": "https://agent.example.com",
253
285
  }
254
286
  ],
255
- 'last_updated': '2025-01-10T12:00:00Z',
256
- 'properties': [
287
+ "last_updated": "2025-01-10T12:00:00Z",
288
+ "properties": [
257
289
  {
258
- 'identifiers': [{'type': 'domain', 'value': 'example.com'}],
259
- 'name': 'Example Site',
260
- 'property_type': 'website',
261
- 'publisher_domain': 'example.com',
290
+ "identifiers": [{"type": "domain", "value": "example.com"}],
291
+ "name": "Example Site",
292
+ "property_type": "website",
293
+ "publisher_domain": "example.com",
262
294
  }
263
295
  ],
264
- 'tags': {
265
- 'all': {
266
- 'description': 'All properties in this file',
267
- 'name': 'All Properties',
296
+ "tags": {
297
+ "all": {
298
+ "description": "All properties in this file",
299
+ "name": "All Properties",
268
300
  }
269
301
  },
270
302
  },
271
303
  {
272
- '$schema': '/schemas/latest/adagents.json',
273
- 'authorized_agents': [
304
+ "$schema": "/schemas/2.6.0/adagents.json",
305
+ "authorized_agents": [
274
306
  {
275
- 'authorization_type': 'property_tags',
276
- 'authorized_for': 'All Meta properties',
277
- 'property_tags': ['meta_network'],
278
- 'url': 'https://meta-ads.com',
307
+ "authorization_type": "property_tags",
308
+ "authorized_for": "All Meta properties",
309
+ "property_tags": ["meta_network"],
310
+ "url": "https://meta-ads.com",
279
311
  }
280
312
  ],
281
- 'contact': {
282
- 'domain': 'meta.com',
283
- 'email': 'adops@meta.com',
284
- 'name': 'Meta Advertising Operations',
285
- 'seller_id': 'pub-meta-12345',
286
- 'tag_id': '12345',
313
+ "contact": {
314
+ "domain": "meta.com",
315
+ "email": "adops@meta.com",
316
+ "name": "Meta Advertising Operations",
317
+ "seller_id": "pub-meta-12345",
318
+ "tag_id": "12345",
287
319
  },
288
- 'last_updated': '2025-01-10T15:30:00Z',
289
- 'properties': [
320
+ "last_updated": "2025-01-10T15:30:00Z",
321
+ "properties": [
290
322
  {
291
- 'identifiers': [
292
- {'type': 'ios_bundle', 'value': 'com.burbn.instagram'},
293
- {'type': 'android_package', 'value': 'com.instagram.android'},
323
+ "identifiers": [
324
+ {"type": "ios_bundle", "value": "com.burbn.instagram"},
325
+ {"type": "android_package", "value": "com.instagram.android"},
294
326
  ],
295
- 'name': 'Instagram',
296
- 'property_type': 'mobile_app',
297
- 'publisher_domain': 'instagram.com',
298
- 'tags': ['meta_network', 'social_media'],
327
+ "name": "Instagram",
328
+ "property_type": "mobile_app",
329
+ "publisher_domain": "instagram.com",
330
+ "tags": ["meta_network", "social_media"],
299
331
  },
300
332
  {
301
- 'identifiers': [
302
- {'type': 'ios_bundle', 'value': 'com.facebook.Facebook'},
303
- {'type': 'android_package', 'value': 'com.facebook.katana'},
333
+ "identifiers": [
334
+ {"type": "ios_bundle", "value": "com.facebook.Facebook"},
335
+ {"type": "android_package", "value": "com.facebook.katana"},
304
336
  ],
305
- 'name': 'Facebook',
306
- 'property_type': 'mobile_app',
307
- 'publisher_domain': 'facebook.com',
308
- 'tags': ['meta_network', 'social_media'],
337
+ "name": "Facebook",
338
+ "property_type": "mobile_app",
339
+ "publisher_domain": "facebook.com",
340
+ "tags": ["meta_network", "social_media"],
309
341
  },
310
342
  {
311
- 'identifiers': [
312
- {'type': 'ios_bundle', 'value': 'net.whatsapp.WhatsApp'},
313
- {'type': 'android_package', 'value': 'com.whatsapp'},
343
+ "identifiers": [
344
+ {"type": "ios_bundle", "value": "net.whatsapp.WhatsApp"},
345
+ {"type": "android_package", "value": "com.whatsapp"},
314
346
  ],
315
- 'name': 'WhatsApp',
316
- 'property_type': 'mobile_app',
317
- 'publisher_domain': 'whatsapp.com',
318
- 'tags': ['meta_network', 'messaging'],
347
+ "name": "WhatsApp",
348
+ "property_type": "mobile_app",
349
+ "publisher_domain": "whatsapp.com",
350
+ "tags": ["meta_network", "messaging"],
319
351
  },
320
352
  ],
321
- 'tags': {
322
- 'messaging': {
323
- 'description': 'Messaging and communication apps',
324
- 'name': 'Messaging Apps',
353
+ "tags": {
354
+ "messaging": {
355
+ "description": "Messaging and communication apps",
356
+ "name": "Messaging Apps",
325
357
  },
326
- 'meta_network': {
327
- 'description': 'All Meta-owned properties',
328
- 'name': 'Meta Network',
358
+ "meta_network": {
359
+ "description": "All Meta-owned properties",
360
+ "name": "Meta Network",
329
361
  },
330
- 'social_media': {
331
- 'description': 'Social networking applications',
332
- 'name': 'Social Media Apps',
362
+ "social_media": {
363
+ "description": "Social networking applications",
364
+ "name": "Social Media Apps",
333
365
  },
334
366
  },
335
367
  },
336
368
  {
337
- '$schema': '/schemas/latest/adagents.json',
338
- 'authorized_agents': [
369
+ "$schema": "/schemas/2.6.0/adagents.json",
370
+ "authorized_agents": [
339
371
  {
340
- 'authorization_type': 'property_tags',
341
- 'authorized_for': 'Tumblr corporate properties only',
342
- 'property_tags': ['corporate'],
343
- 'url': 'https://tumblr-sales.com',
372
+ "authorization_type": "property_tags",
373
+ "authorized_for": "Tumblr corporate properties only",
374
+ "property_tags": ["corporate"],
375
+ "url": "https://tumblr-sales.com",
344
376
  }
345
377
  ],
346
- 'contact': {'name': 'Tumblr Advertising'},
347
- 'last_updated': '2025-01-10T16:00:00Z',
348
- 'properties': [
378
+ "contact": {"name": "Tumblr Advertising"},
379
+ "last_updated": "2025-01-10T16:00:00Z",
380
+ "properties": [
349
381
  {
350
- 'identifiers': [{'type': 'domain', 'value': 'tumblr.com'}],
351
- 'name': 'Tumblr Corporate',
352
- 'property_type': 'website',
353
- 'publisher_domain': 'tumblr.com',
354
- 'tags': ['corporate'],
382
+ "identifiers": [{"type": "domain", "value": "tumblr.com"}],
383
+ "name": "Tumblr Corporate",
384
+ "property_type": "website",
385
+ "publisher_domain": "tumblr.com",
386
+ "tags": ["corporate"],
355
387
  }
356
388
  ],
357
- 'tags': {
358
- 'corporate': {
359
- 'description': 'Tumblr-owned corporate properties (not user blogs)',
360
- 'name': 'Corporate Properties',
389
+ "tags": {
390
+ "corporate": {
391
+ "description": "Tumblr-owned corporate properties (not user blogs)",
392
+ "name": "Corporate Properties",
361
393
  }
362
394
  },
363
395
  },
364
396
  {
365
- '$schema': '/schemas/latest/adagents.json',
366
- 'authorized_agents': [
397
+ "$schema": "/schemas/2.6.0/adagents.json",
398
+ "authorized_agents": [
367
399
  {
368
- 'authorization_type': 'publisher_properties',
369
- 'authorized_for': 'CNN CTV properties via publisher authorization',
370
- 'publisher_properties': [
400
+ "authorization_type": "publisher_properties",
401
+ "authorized_for": "CNN CTV properties via publisher authorization",
402
+ "publisher_properties": [
371
403
  {
372
- 'property_ids': ['cnn_ctv_app'],
373
- 'publisher_domain': 'cnn.com',
374
- 'selection_type': 'by_id',
404
+ "property_ids": ["cnn_ctv_app"],
405
+ "publisher_domain": "cnn.com",
406
+ "selection_type": "by_id",
375
407
  }
376
408
  ],
377
- 'url': 'https://agent.example/api',
409
+ "url": "https://agent.example/api",
378
410
  },
379
411
  {
380
- 'authorization_type': 'publisher_properties',
381
- 'authorized_for': 'All CTV properties from multiple publishers',
382
- 'publisher_properties': [
412
+ "authorization_type": "publisher_properties",
413
+ "authorized_for": "All CTV properties from multiple publishers",
414
+ "publisher_properties": [
383
415
  {
384
- 'property_tags': ['ctv'],
385
- 'publisher_domain': 'cnn.com',
386
- 'selection_type': 'by_tag',
416
+ "property_tags": ["ctv"],
417
+ "publisher_domain": "cnn.com",
418
+ "selection_type": "by_tag",
387
419
  },
388
420
  {
389
- 'property_tags': ['ctv'],
390
- 'publisher_domain': 'espn.com',
391
- 'selection_type': 'by_tag',
421
+ "property_tags": ["ctv"],
422
+ "publisher_domain": "espn.com",
423
+ "selection_type": "by_tag",
392
424
  },
393
425
  ],
394
- 'url': 'https://agent.example/api',
426
+ "url": "https://agent.example/api",
395
427
  },
396
428
  ],
397
- 'contact': {
398
- 'domain': 'agent.example',
399
- 'email': 'sales@agent.example',
400
- 'name': 'Example Third-Party Sales Agent',
429
+ "contact": {
430
+ "domain": "agent.example",
431
+ "email": "sales@agent.example",
432
+ "name": "Example Third-Party Sales Agent",
433
+ },
434
+ "last_updated": "2025-01-10T17:00:00Z",
435
+ },
436
+ {
437
+ "$schema": "/schemas/2.6.0/adagents.json",
438
+ "authorized_agents": [
439
+ {
440
+ "authorization_type": "property_tags",
441
+ "authorized_for": "All news properties",
442
+ "property_tags": ["news"],
443
+ "url": "https://sales.news.example.com",
444
+ }
445
+ ],
446
+ "contact": {
447
+ "domain": "news.example.com",
448
+ "email": "adops@news.example.com",
449
+ "name": "Premium News Publisher",
450
+ },
451
+ "last_updated": "2025-01-10T18:00:00Z",
452
+ "properties": [
453
+ {
454
+ "identifiers": [{"type": "domain", "value": "news.example.com"}],
455
+ "name": "News Example",
456
+ "property_type": "website",
457
+ "publisher_domain": "news.example.com",
458
+ "tags": ["premium", "news"],
459
+ }
460
+ ],
461
+ "property_features": [
462
+ {
463
+ "features": ["carbon_score", "sustainability_grade"],
464
+ "name": "Scope3",
465
+ "publisher_id": "pub_news_12345",
466
+ "url": "https://api.scope3.com",
467
+ },
468
+ {
469
+ "features": [
470
+ "tag_certified_against_fraud",
471
+ "tag_brand_safety_certified",
472
+ ],
473
+ "name": "TAG",
474
+ "url": "https://api.tagtoday.net",
475
+ },
476
+ {
477
+ "features": ["gdpr_compliant", "tcf_registered", "ccpa_compliant"],
478
+ "name": "OneTrust",
479
+ "publisher_id": "ot_news_67890",
480
+ "url": "https://api.onetrust.com",
481
+ },
482
+ ],
483
+ "tags": {
484
+ "news": {
485
+ "description": "News and journalism content",
486
+ "name": "News Properties",
487
+ },
488
+ "premium": {
489
+ "description": "High-quality, brand-safe properties",
490
+ "name": "Premium Properties",
491
+ },
401
492
  },
402
- 'last_updated': '2025-01-10T17:00:00Z',
403
493
  },
404
494
  ],
405
- title='Authorized Sales Agents',
495
+ title="Authorized Sales Agents",
406
496
  ),
407
497
  ]