adcp 2.13.0__py3-none-any.whl → 2.14.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 (61) hide show
  1. adcp/__init__.py +14 -1
  2. adcp/adagents.py +53 -9
  3. adcp/client.py +361 -57
  4. adcp/protocols/mcp.py +1 -3
  5. adcp/types/__init__.py +9 -33
  6. adcp/types/_generated.py +82 -13
  7. adcp/types/aliases.py +23 -0
  8. adcp/types/generated_poc/adagents.py +9 -13
  9. adcp/types/generated_poc/core/activation_key.py +12 -2
  10. adcp/types/generated_poc/core/assets/daast_asset.py +12 -2
  11. adcp/types/generated_poc/core/assets/image_asset.py +9 -5
  12. adcp/types/generated_poc/core/assets/vast_asset.py +12 -2
  13. adcp/types/generated_poc/core/assets/video_asset.py +9 -5
  14. adcp/types/generated_poc/core/async_response_data.py +72 -0
  15. adcp/types/generated_poc/core/brand_manifest_ref.py +35 -0
  16. adcp/types/generated_poc/core/creative_asset.py +4 -6
  17. adcp/types/generated_poc/core/creative_manifest.py +4 -6
  18. adcp/types/generated_poc/core/deployment.py +16 -8
  19. adcp/types/generated_poc/core/destination.py +12 -2
  20. adcp/types/generated_poc/core/format.py +3 -3
  21. adcp/types/generated_poc/core/{webhook_payload.py → mcp_webhook_payload.py} +8 -33
  22. adcp/types/generated_poc/core/pricing_option.py +51 -0
  23. adcp/types/generated_poc/core/product.py +4 -29
  24. adcp/types/generated_poc/core/promoted_offerings.py +5 -21
  25. adcp/types/generated_poc/core/property.py +4 -6
  26. adcp/types/generated_poc/core/publisher_property_selector.py +15 -2
  27. adcp/types/generated_poc/core/push_notification_config.py +1 -4
  28. adcp/types/generated_poc/core/start_timing.py +18 -0
  29. adcp/types/generated_poc/core/sub_asset.py +12 -2
  30. adcp/types/generated_poc/creative/preview_creative_response.py +3 -14
  31. adcp/types/generated_poc/creative/preview_render.py +3 -11
  32. adcp/types/generated_poc/media_buy/create_media_buy_async_response_input_required.py +37 -0
  33. adcp/types/generated_poc/media_buy/create_media_buy_async_response_submitted.py +19 -0
  34. adcp/types/generated_poc/media_buy/create_media_buy_async_response_working.py +31 -0
  35. adcp/types/generated_poc/media_buy/create_media_buy_request.py +7 -26
  36. adcp/types/generated_poc/media_buy/get_media_buy_delivery_response.py +4 -2
  37. adcp/types/generated_poc/media_buy/get_products_async_response_input_required.py +38 -0
  38. adcp/types/generated_poc/media_buy/get_products_async_response_submitted.py +24 -0
  39. adcp/types/generated_poc/media_buy/get_products_async_response_working.py +35 -0
  40. adcp/types/generated_poc/media_buy/get_products_request.py +5 -20
  41. adcp/types/generated_poc/media_buy/list_creatives_response.py +5 -7
  42. adcp/types/generated_poc/media_buy/sync_creatives_async_response_input_required.py +31 -0
  43. adcp/types/generated_poc/media_buy/sync_creatives_async_response_submitted.py +19 -0
  44. adcp/types/generated_poc/media_buy/sync_creatives_async_response_working.py +37 -0
  45. adcp/types/generated_poc/media_buy/update_media_buy_async_response_input_required.py +30 -0
  46. adcp/types/generated_poc/media_buy/update_media_buy_async_response_submitted.py +19 -0
  47. adcp/types/generated_poc/media_buy/update_media_buy_async_response_working.py +31 -0
  48. adcp/types/generated_poc/media_buy/update_media_buy_request.py +4 -14
  49. adcp/types/generated_poc/signals/activate_signal_request.py +2 -2
  50. adcp/types/generated_poc/signals/activate_signal_response.py +2 -2
  51. adcp/types/generated_poc/signals/get_signals_request.py +2 -2
  52. adcp/types/generated_poc/signals/get_signals_response.py +2 -3
  53. adcp/utils/preview_cache.py +6 -4
  54. adcp/webhooks.py +508 -0
  55. {adcp-2.13.0.dist-info → adcp-2.14.0.dist-info}/METADATA +2 -2
  56. {adcp-2.13.0.dist-info → adcp-2.14.0.dist-info}/RECORD +60 -44
  57. adcp/types/generated_poc/core/dimensions.py +0 -18
  58. {adcp-2.13.0.dist-info → adcp-2.14.0.dist-info}/WHEEL +0 -0
  59. {adcp-2.13.0.dist-info → adcp-2.14.0.dist-info}/entry_points.txt +0 -0
  60. {adcp-2.13.0.dist-info → adcp-2.14.0.dist-info}/licenses/LICENSE +0 -0
  61. {adcp-2.13.0.dist-info → adcp-2.14.0.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,13 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/assets/vast_asset.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated, Literal
8
8
 
9
9
  from adcp.types.base import AdCPBaseModel
10
- from pydantic import AnyUrl, ConfigDict, Field
10
+ from pydantic import AnyUrl, ConfigDict, Field, RootModel
11
11
 
12
12
  from ...enums import vast_tracking_event
13
13
  from ...enums import vast_version as vast_version_1
@@ -61,3 +61,13 @@ class VastAsset2(AdCPBaseModel):
61
61
  bool | None,
62
62
  Field(description='Whether VPAID (Video Player-Ad Interface Definition) is supported'),
63
63
  ] = None
64
+
65
+
66
+ class VastAsset(RootModel[VastAsset1 | VastAsset2]):
67
+ root: Annotated[
68
+ VastAsset1 | VastAsset2,
69
+ Field(
70
+ description='VAST (Video Ad Serving Template) tag for third-party video ad serving',
71
+ title='VAST Asset',
72
+ ),
73
+ ]
@@ -1,17 +1,19 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/assets/video_asset.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-18T20:00:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated
8
8
 
9
- from pydantic import AnyUrl, Field
9
+ from adcp.types.base import AdCPBaseModel
10
+ from pydantic import AnyUrl, ConfigDict, Field
10
11
 
11
- from ..dimensions import Dimensions
12
12
 
13
-
14
- class VideoAsset(Dimensions):
13
+ class VideoAsset(AdCPBaseModel):
14
+ model_config = ConfigDict(
15
+ extra='forbid',
16
+ )
15
17
  bitrate_kbps: Annotated[
16
18
  int | None, Field(description='Video bitrate in kilobits per second', ge=1)
17
19
  ] = None
@@ -21,4 +23,6 @@ class VideoAsset(Dimensions):
21
23
  format: Annotated[str | None, Field(description='Video file format (mp4, webm, mov, etc.)')] = (
22
24
  None
23
25
  )
26
+ height: Annotated[int, Field(description='Height in pixels', ge=1)]
24
27
  url: Annotated[AnyUrl, Field(description='URL to the video asset')]
28
+ width: Annotated[int, Field(description='Width in pixels', ge=1)]
@@ -0,0 +1,72 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: core/async_response_data.json
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Annotated
8
+
9
+ from pydantic import Field, RootModel
10
+
11
+ from ..media_buy import (
12
+ create_media_buy_async_response_input_required,
13
+ create_media_buy_async_response_submitted,
14
+ create_media_buy_async_response_working,
15
+ create_media_buy_response,
16
+ get_products_async_response_input_required,
17
+ get_products_async_response_submitted,
18
+ get_products_async_response_working,
19
+ get_products_response,
20
+ sync_creatives_async_response_input_required,
21
+ sync_creatives_async_response_submitted,
22
+ sync_creatives_async_response_working,
23
+ sync_creatives_response,
24
+ update_media_buy_async_response_input_required,
25
+ update_media_buy_async_response_submitted,
26
+ update_media_buy_async_response_working,
27
+ update_media_buy_response,
28
+ )
29
+
30
+
31
+ class AdcpAsyncResponseData(
32
+ RootModel[
33
+ get_products_response.GetProductsResponse
34
+ | get_products_async_response_working.GetProductsWorking
35
+ | get_products_async_response_input_required.GetProductsInputRequired
36
+ | get_products_async_response_submitted.GetProductsSubmitted
37
+ | create_media_buy_response.CreateMediaBuyResponse
38
+ | create_media_buy_async_response_working.CreateMediaBuyWorking
39
+ | create_media_buy_async_response_input_required.CreateMediaBuyInputRequired
40
+ | create_media_buy_async_response_submitted.CreateMediaBuySubmitted
41
+ | update_media_buy_response.UpdateMediaBuyResponse
42
+ | update_media_buy_async_response_working.UpdateMediaBuyWorking
43
+ | update_media_buy_async_response_input_required.UpdateMediaBuyInputRequired
44
+ | update_media_buy_async_response_submitted.UpdateMediaBuySubmitted
45
+ | sync_creatives_response.SyncCreativesResponse
46
+ | sync_creatives_async_response_working.SyncCreativesWorking
47
+ | sync_creatives_async_response_input_required.SyncCreativesInputRequired
48
+ | sync_creatives_async_response_submitted.SyncCreativesSubmitted
49
+ ]
50
+ ):
51
+ root: Annotated[
52
+ get_products_response.GetProductsResponse
53
+ | get_products_async_response_working.GetProductsWorking
54
+ | get_products_async_response_input_required.GetProductsInputRequired
55
+ | get_products_async_response_submitted.GetProductsSubmitted
56
+ | create_media_buy_response.CreateMediaBuyResponse
57
+ | create_media_buy_async_response_working.CreateMediaBuyWorking
58
+ | create_media_buy_async_response_input_required.CreateMediaBuyInputRequired
59
+ | create_media_buy_async_response_submitted.CreateMediaBuySubmitted
60
+ | update_media_buy_response.UpdateMediaBuyResponse
61
+ | update_media_buy_async_response_working.UpdateMediaBuyWorking
62
+ | update_media_buy_async_response_input_required.UpdateMediaBuyInputRequired
63
+ | update_media_buy_async_response_submitted.UpdateMediaBuySubmitted
64
+ | sync_creatives_response.SyncCreativesResponse
65
+ | sync_creatives_async_response_working.SyncCreativesWorking
66
+ | sync_creatives_async_response_input_required.SyncCreativesInputRequired
67
+ | sync_creatives_async_response_submitted.SyncCreativesSubmitted,
68
+ Field(
69
+ description='Union of all possible data payloads for async task webhook responses. For completed/failed statuses, use the main task response schema. For working/input-required/submitted, use the status-specific schemas.',
70
+ title='AdCP Async Response Data',
71
+ ),
72
+ ]
@@ -0,0 +1,35 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: core/brand_manifest_ref.json
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Annotated
8
+
9
+ from pydantic import AnyUrl, Field, RootModel
10
+
11
+ from . import brand_manifest
12
+
13
+
14
+ class BrandManifestReference(RootModel[brand_manifest.BrandManifest | AnyUrl]):
15
+ root: Annotated[
16
+ brand_manifest.BrandManifest | AnyUrl,
17
+ Field(
18
+ description='Brand manifest provided either as an inline object or a URL string pointing to a hosted manifest',
19
+ examples=[
20
+ {
21
+ 'data': {
22
+ 'colors': {'primary': '#FF6B35'},
23
+ 'name': 'ACME Corporation',
24
+ 'url': 'https://acmecorp.com',
25
+ },
26
+ 'description': 'Inline brand manifest',
27
+ },
28
+ {
29
+ 'data': 'https://cdn.acmecorp.com/brand-manifest.json',
30
+ 'description': 'URL string reference to hosted manifest',
31
+ },
32
+ ],
33
+ title='Brand Manifest Reference',
34
+ ),
35
+ ]
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/creative_asset.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -59,12 +59,10 @@ class CreativeAsset(AdCPBaseModel):
59
59
  | html_asset.HtmlAsset
60
60
  | css_asset.CssAsset
61
61
  | javascript_asset.JavascriptAsset
62
+ | vast_asset.VastAsset
63
+ | daast_asset.DaastAsset
62
64
  | promoted_offerings.PromotedOfferings
63
- | url_asset.UrlAsset
64
- | vast_asset.VastAsset1
65
- | vast_asset.VastAsset2
66
- | daast_asset.DaastAsset1
67
- | daast_asset.DaastAsset2,
65
+ | url_asset.UrlAsset,
68
66
  ],
69
67
  Field(description='Assets required by the format, keyed by asset_role'),
70
68
  ]
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/creative_manifest.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -37,17 +37,15 @@ class CreativeManifest(AdCPBaseModel):
37
37
  image_asset.ImageAsset
38
38
  | video_asset.VideoAsset
39
39
  | audio_asset.AudioAsset
40
+ | vast_asset.VastAsset
40
41
  | text_asset.TextAsset
41
42
  | url_asset.UrlAsset
42
43
  | html_asset.HtmlAsset
43
44
  | javascript_asset.JavascriptAsset
44
45
  | webhook_asset.WebhookAsset
45
46
  | css_asset.CssAsset
46
- | promoted_offerings.PromotedOfferings
47
- | vast_asset.VastAsset1
48
- | vast_asset.VastAsset2
49
- | daast_asset.DaastAsset1
50
- | daast_asset.DaastAsset2,
47
+ | daast_asset.DaastAsset
48
+ | promoted_offerings.PromotedOfferings,
51
49
  ],
52
50
  Field(
53
51
  description="Map of asset IDs to actual asset content. Each key MUST match an asset_id from the format's assets_required 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: Creative manifest validation MUST be performed in the context of the format specification. The format defines what type each asset_id should be, which eliminates any validation ambiguity."
@@ -1,13 +1,13 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/deployment.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated, Literal
8
8
 
9
9
  from adcp.types.base import AdCPBaseModel
10
- from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field
10
+ from pydantic import AnyUrl, AwareDatetime, ConfigDict, Field, RootModel
11
11
 
12
12
  from . import activation_key as activation_key_1
13
13
 
@@ -18,10 +18,9 @@ class Deployment1(AdCPBaseModel):
18
18
  )
19
19
  account: Annotated[str | None, Field(description='Account identifier if applicable')] = None
20
20
  activation_key: Annotated[
21
- activation_key_1.ActivationKey1 | activation_key_1.ActivationKey2 | None,
21
+ activation_key_1.ActivationKey | None,
22
22
  Field(
23
- description='The key to use for targeting. Only present if is_live=true AND requester has access to this deployment.',
24
- title='Activation Key',
23
+ description='The key to use for targeting. Only present if is_live=true AND requester has access to this deployment.'
25
24
  ),
26
25
  ] = None
27
26
  deployed_at: Annotated[
@@ -51,10 +50,9 @@ class Deployment2(AdCPBaseModel):
51
50
  )
52
51
  account: Annotated[str | None, Field(description='Account identifier if applicable')] = None
53
52
  activation_key: Annotated[
54
- activation_key_1.ActivationKey1 | activation_key_1.ActivationKey2 | None,
53
+ activation_key_1.ActivationKey | None,
55
54
  Field(
56
- description='The key to use for targeting. Only present if is_live=true AND requester has access to this deployment.',
57
- title='Activation Key',
55
+ description='The key to use for targeting. Only present if is_live=true AND requester has access to this deployment.'
58
56
  ),
59
57
  ] = None
60
58
  agent_url: Annotated[AnyUrl, Field(description='URL identifying the deployment agent')]
@@ -76,3 +74,13 @@ class Deployment2(AdCPBaseModel):
76
74
  Literal['agent'],
77
75
  Field(description='Discriminator indicating this is an agent URL-based deployment'),
78
76
  ]
77
+
78
+
79
+ class Deployment(RootModel[Deployment1 | Deployment2]):
80
+ root: Annotated[
81
+ Deployment1 | Deployment2,
82
+ Field(
83
+ description='A signal deployment to a specific deployment target with activation status and key',
84
+ title='Deployment',
85
+ ),
86
+ ]
@@ -1,13 +1,13 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/destination.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated, Literal
8
8
 
9
9
  from adcp.types.base import AdCPBaseModel
10
- from pydantic import AnyUrl, ConfigDict, Field
10
+ from pydantic import AnyUrl, ConfigDict, Field, RootModel
11
11
 
12
12
 
13
13
  class Destination1(AdCPBaseModel):
@@ -41,3 +41,13 @@ class Destination2(AdCPBaseModel):
41
41
  Literal['agent'],
42
42
  Field(description='Discriminator indicating this is an agent URL-based deployment'),
43
43
  ]
44
+
45
+
46
+ class Destination(RootModel[Destination1 | Destination2]):
47
+ root: Annotated[
48
+ Destination1 | Destination2,
49
+ Field(
50
+ description='A deployment target where signals can be activated (DSP, sales agent, etc.)',
51
+ title='Destination',
52
+ ),
53
+ ]
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/format.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-18T20:00:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -123,12 +123,12 @@ class Renders(AdCPBaseModel):
123
123
  ]
124
124
 
125
125
 
126
- Dimensions2 = Dimensions
126
+ Dimensions1 = Dimensions
127
127
 
128
128
 
129
129
  class Renders1(AdCPBaseModel):
130
130
  dimensions: Annotated[
131
- Dimensions2 | None, Field(description='Dimensions for this rendered piece (in pixels)')
131
+ Dimensions1 | None, Field(description='Dimensions for this rendered piece (in pixels)')
132
132
  ] = None
133
133
  parameters_from_format_id: Annotated[
134
134
  Literal[True],
@@ -1,35 +1,20 @@
1
1
  # generated by datamodel-codegen:
2
- # filename: core/webhook_payload.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
2
+ # filename: core/mcp_webhook_payload.json
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- from typing import Annotated, Any
7
+ from typing import Annotated
8
8
 
9
9
  from adcp.types.base import AdCPBaseModel
10
10
  from pydantic import AwareDatetime, ConfigDict, Field
11
11
 
12
12
  from ..enums import adcp_domain, task_status
13
13
  from ..enums import task_type as task_type_1
14
+ from . import async_response_data
14
15
 
15
16
 
16
- class Progress(AdCPBaseModel):
17
- model_config = ConfigDict(
18
- extra='forbid',
19
- )
20
- current_step: Annotated[
21
- str | None, Field(description='Current step or phase of the operation')
22
- ] = None
23
- percentage: Annotated[
24
- float | None, Field(description='Completion percentage (0-100)', ge=0.0, le=100.0)
25
- ] = None
26
- step_number: Annotated[int | None, Field(description='Current step number', ge=1)] = None
27
- total_steps: Annotated[
28
- int | None, Field(description='Total number of steps in the operation', ge=1)
29
- ] = None
30
-
31
-
32
- class WebhookPayload(AdCPBaseModel):
17
+ class McpWebhookPayload(AdCPBaseModel):
33
18
  model_config = ConfigDict(
34
19
  extra='allow',
35
20
  )
@@ -45,10 +30,6 @@ class WebhookPayload(AdCPBaseModel):
45
30
  description='AdCP domain this task belongs to. Helps classify the operation type at a high level.'
46
31
  ),
47
32
  ] = None
48
- error: Annotated[
49
- str | None,
50
- Field(description="Error message for failed tasks. Only present when status is 'failed'."),
51
- ] = None
52
33
  message: Annotated[
53
34
  str | None,
54
35
  Field(
@@ -61,22 +42,16 @@ class WebhookPayload(AdCPBaseModel):
61
42
  description='Publisher-defined operation identifier correlating a sequence of task updates across webhooks.'
62
43
  ),
63
44
  ] = None
64
- progress: Annotated[
65
- Progress | None,
66
- Field(
67
- description="Progress information for tasks still in 'working' state. Rarely seen in webhooks since 'working' tasks typically complete synchronously, but may appear if a task transitions from 'submitted' to 'working'."
68
- ),
69
- ] = None
70
45
  result: Annotated[
71
- dict[str, Any] | None,
46
+ async_response_data.AdcpAsyncResponseData | None,
72
47
  Field(
73
- description='Task-specific payload for this status update. Validated against the appropriate response schema based on task_type.'
48
+ description='Task-specific payload matching the status. For completed/failed, contains the full task response. For working/input-required/submitted, contains status-specific data. This is the data layer that AdCP specs - same structure used in A2A status.message.parts[].data.'
74
49
  ),
75
50
  ] = None
76
51
  status: Annotated[
77
52
  task_status.TaskStatus,
78
53
  Field(
79
- description='Current task status. Webhooks are only triggered for status changes after initial submission (e.g., submitted → input-required, submitted → completed, submitted → failed).'
54
+ description='Current task status. Webhooks are triggered for status changes after initial submission.'
80
55
  ),
81
56
  ]
82
57
  task_id: Annotated[
@@ -0,0 +1,51 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: core/pricing_option.json
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Annotated
8
+
9
+ from pydantic import Field, RootModel
10
+
11
+ from ..pricing_options import (
12
+ cpc_option,
13
+ cpcv_option,
14
+ cpm_auction_option,
15
+ cpm_fixed_option,
16
+ cpp_option,
17
+ cpv_option,
18
+ flat_rate_option,
19
+ vcpm_auction_option,
20
+ vcpm_fixed_option,
21
+ )
22
+
23
+
24
+ class PricingOption(
25
+ RootModel[
26
+ cpm_fixed_option.CpmFixedRatePricingOption
27
+ | cpm_auction_option.CpmAuctionPricingOption
28
+ | vcpm_fixed_option.VcpmFixedRatePricingOption
29
+ | vcpm_auction_option.VcpmAuctionPricingOption
30
+ | cpc_option.CpcPricingOption
31
+ | cpcv_option.CpcvPricingOption
32
+ | cpv_option.CpvPricingOption
33
+ | cpp_option.CppPricingOption
34
+ | flat_rate_option.FlatRatePricingOption
35
+ ]
36
+ ):
37
+ root: Annotated[
38
+ cpm_fixed_option.CpmFixedRatePricingOption
39
+ | cpm_auction_option.CpmAuctionPricingOption
40
+ | vcpm_fixed_option.VcpmFixedRatePricingOption
41
+ | vcpm_auction_option.VcpmAuctionPricingOption
42
+ | cpc_option.CpcPricingOption
43
+ | cpcv_option.CpcvPricingOption
44
+ | cpv_option.CpvPricingOption
45
+ | cpp_option.CppPricingOption
46
+ | flat_rate_option.FlatRatePricingOption,
47
+ Field(
48
+ description='A pricing model option offered by a publisher for a product. Each pricing model has its own schema with model-specific requirements.',
49
+ title='Pricing Option',
50
+ ),
51
+ ]
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/product.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -10,22 +10,11 @@ from adcp.types.base import AdCPBaseModel
10
10
  from pydantic import AwareDatetime, ConfigDict, Field
11
11
 
12
12
  from ..enums import delivery_type as delivery_type_1
13
- from ..pricing_options import (
14
- cpc_option,
15
- cpcv_option,
16
- cpm_auction_option,
17
- cpm_fixed_option,
18
- cpp_option,
19
- cpv_option,
20
- flat_rate_option,
21
- vcpm_auction_option,
22
- vcpm_fixed_option,
23
- )
24
13
  from . import creative_policy as creative_policy_1
25
14
  from . import ext as ext_1
26
15
  from . import format_id as format_id_1
27
16
  from . import measurement as measurement_1
28
- from . import placement, publisher_property_selector
17
+ from . import placement, pricing_option, publisher_property_selector
29
18
  from . import reporting_capabilities as reporting_capabilities_1
30
19
 
31
20
 
@@ -124,17 +113,7 @@ class Product(AdCPBaseModel):
124
113
  ),
125
114
  ] = None
126
115
  pricing_options: Annotated[
127
- list[
128
- cpm_fixed_option.CpmFixedRatePricingOption
129
- | cpm_auction_option.CpmAuctionPricingOption
130
- | vcpm_fixed_option.VcpmFixedRatePricingOption
131
- | vcpm_auction_option.VcpmAuctionPricingOption
132
- | cpc_option.CpcPricingOption
133
- | cpcv_option.CpcvPricingOption
134
- | cpv_option.CpvPricingOption
135
- | cpp_option.CppPricingOption
136
- | flat_rate_option.FlatRatePricingOption
137
- ],
116
+ list[pricing_option.PricingOption],
138
117
  Field(description='Available pricing models for this product', min_length=1),
139
118
  ]
140
119
  product_card: Annotated[
@@ -151,11 +130,7 @@ class Product(AdCPBaseModel):
151
130
  ] = None
152
131
  product_id: Annotated[str, Field(description='Unique identifier for the product')]
153
132
  publisher_properties: Annotated[
154
- list[
155
- publisher_property_selector.PublisherPropertySelector1
156
- | publisher_property_selector.PublisherPropertySelector2
157
- | publisher_property_selector.PublisherPropertySelector3
158
- ],
133
+ list[publisher_property_selector.PublisherPropertySelector],
159
134
  Field(
160
135
  description="Publisher properties covered by this product. Buyers fetch actual property definitions from each publisher's adagents.json and validate agent authorization. Selection patterns mirror the authorization patterns in adagents.json for consistency.",
161
136
  min_length=1,
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/promoted_offerings.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -8,10 +8,9 @@ from enum import Enum
8
8
  from typing import Annotated, Any
9
9
 
10
10
  from adcp.types.base import AdCPBaseModel
11
- from pydantic import AnyUrl, ConfigDict, Field
11
+ from pydantic import ConfigDict, Field
12
12
 
13
- from . import brand_manifest as brand_manifest_1
14
- from . import promoted_products
13
+ from . import brand_manifest_ref, promoted_products
15
14
 
16
15
 
17
16
  class AssetType(Enum):
@@ -68,24 +67,9 @@ class PromotedOfferings(AdCPBaseModel):
68
67
  Field(description='Selectors to choose specific assets from the brand manifest'),
69
68
  ] = None
70
69
  brand_manifest: Annotated[
71
- brand_manifest_1.BrandManifest | AnyUrl,
70
+ brand_manifest_ref.BrandManifestReference,
72
71
  Field(
73
- description='Brand information manifest containing assets, themes, and guidelines. Can be provided inline or as a URL reference to a hosted manifest.',
74
- examples=[
75
- {
76
- 'data': {
77
- 'colors': {'primary': '#FF6B35'},
78
- 'name': 'ACME Corporation',
79
- 'url': 'https://acmecorp.com',
80
- },
81
- 'description': 'Inline brand manifest',
82
- },
83
- {
84
- 'data': 'https://cdn.acmecorp.com/brand-manifest.json',
85
- 'description': 'URL string reference to hosted manifest',
86
- },
87
- ],
88
- title='Brand Manifest Reference',
72
+ description='Brand information manifest containing assets, themes, and guidelines. Can be provided inline or as a URL reference to a hosted manifest.'
89
73
  ),
90
74
  ]
91
75
  offerings: Annotated[
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/property.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -11,6 +11,7 @@ from pydantic import ConfigDict, Field
11
11
 
12
12
  from ..enums import identifier_types
13
13
  from ..enums import property_type as property_type_1
14
+ from . import property_id as property_id_1
14
15
  from . import property_tag
15
16
 
16
17
 
@@ -39,12 +40,9 @@ class Property(AdCPBaseModel):
39
40
  ]
40
41
  name: Annotated[str, Field(description='Human-readable property name')]
41
42
  property_id: Annotated[
42
- str | None,
43
+ property_id_1.PropertyId | None,
43
44
  Field(
44
- description='Unique identifier for this property (optional). Enables referencing properties by ID instead of repeating full objects.',
45
- examples=['cnn_ctv_app', 'homepage', 'mobile_ios', 'instagram'],
46
- pattern='^[a-z0-9_]+$',
47
- title='Property ID',
45
+ description='Unique identifier for this property (optional). Enables referencing properties by ID instead of repeating full objects.'
48
46
  ),
49
47
  ] = None
50
48
  property_type: Annotated[
@@ -1,13 +1,13 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/publisher_property_selector.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-11T15:09:37+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
7
  from typing import Annotated, Literal
8
8
 
9
9
  from adcp.types.base import AdCPBaseModel
10
- from pydantic import ConfigDict, Field
10
+ from pydantic import ConfigDict, Field, RootModel
11
11
 
12
12
  from . import property_id, property_tag
13
13
 
@@ -73,3 +73,16 @@ class PublisherPropertySelector3(AdCPBaseModel):
73
73
  selection_type: Annotated[
74
74
  Literal['by_tag'], Field(description='Discriminator indicating selection by property tags')
75
75
  ]
76
+
77
+
78
+ class PublisherPropertySelector(
79
+ RootModel[PublisherPropertySelector1 | PublisherPropertySelector2 | PublisherPropertySelector3]
80
+ ):
81
+ root: Annotated[
82
+ PublisherPropertySelector1 | PublisherPropertySelector2 | PublisherPropertySelector3,
83
+ Field(
84
+ description="Selects properties from a publisher's adagents.json. Used for both product definitions and agent authorization. Supports three selection patterns: all properties, specific IDs, or by tags.",
85
+ discriminator='selection_type',
86
+ title='Publisher Property Selector',
87
+ ),
88
+ ]
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: core/push_notification_config.json
3
- # timestamp: 2025-11-29T12:00:45+00:00
3
+ # timestamp: 2025-12-18T20:00:24+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -34,9 +34,6 @@ class Authentication(AdCPBaseModel):
34
34
 
35
35
 
36
36
  class PushNotificationConfig(AdCPBaseModel):
37
- model_config = ConfigDict(
38
- extra='forbid',
39
- )
40
37
  authentication: Annotated[
41
38
  Authentication,
42
39
  Field(description='Authentication configuration for webhook delivery (A2A-compatible)'),