Module adcp.types.media_buy

AdCP media buy types — curated partial surface.

Media-buy lifecycle types — create / update / get media buys, packages, delivery, pacing, budget, targeting overlays, and media-buy status.

A stable, narrow alternative to importing the whole :mod:adcp.types namespace. Every name here is also exported from :mod:adcp.types; this module simply groups the ones a media buy integration reaches for, and never exposes the internal generated layer.

This module is for curation and discoverability, not a separate performance tier: importing it is cheap, but the first access to any AdCP type (here or via :mod:adcp.types / :mod:adcp) realizes the full generated Pydantic graph — there is no per-domain graph. Use it for a smaller, focused import surface.

from adcp.types.media_buy import CreateMediaBuyRequest

Classes

class AcceptProposalRequest (**data: Any)
Expand source code
class AcceptProposalRequest(AdcpRequest, AdcpVersionEnvelope):
    model_config = ConfigDict(
        extra='forbid',
    )
    idempotency_key: Annotated[
        str, Field(max_length=255, min_length=16, pattern='^[A-Za-z0-9_.:-]{16,255}$')
    ]
    name: Annotated[
        str | None,
        Field(
            description='Human-readable MediaBuy name supplied by the buyer for trafficking UI display and operational communication. When supplied, this value wins over proposal.name; the seller MUST persist it and echo it unchanged on the commitment success response and subsequent get_media_buys reads. It is operational metadata outside accepted_proposal and is not covered by proposal_terms_digest or terms_digest. When an acceptance creates a MediaBuy and name is absent, the seller MAY seed the MediaBuy name from proposal.name only when proposal.name already satisfies the MediaBuy name constraints (non-whitespace and no longer than 255 characters); the seller MUST NOT silently truncate or otherwise rewrite it. A seeded value counts as a name created through AdCP and MUST be reported on commitment and read surfaces. This display label is not an identifier or financial reference.',
            max_length=255,
            min_length=1,
            pattern='\\S',
        ),
    ] = None
    account: canonical_account_ref.CanonicalAccountReference
    proposal_id: Annotated[str, Field(min_length=1)]
    proposal_terms_digest: Annotated[
        str,
        Field(
            description='terms_digest from the committed proposal. The seller MUST atomically verify both ID and digest before acceptance.',
            pattern='^sha256:[A-Za-z0-9_-]{43}$',
        ),
    ]
    total_budget: Annotated[
        TotalBudget | None,
        Field(
            description='Execution amount when the committed proposal defines scalable percentages or constraints rather than a fixed total.'
        ),
    ] = None
    daily_budget_cap: Annotated[
        StrictFloat | None,
        Field(
            description="Optional hard aggregate daily spend ceiling applied when the committed proposal is accepted. It constrains execution without changing the proposal's negotiated pricing.",
            ge=0.0,
        ),
    ] = None
    budget_cap_timezone: Annotated[
        str | None,
        Field(
            description='Optional shared IANA cap-day timezone override. Requires buyer_timezone_override support. When omitted, budget_capping.timezone_basis selects Account.timezone or fixed_timezone.',
            min_length=1,
        ),
    ] = None
    io_acceptance: IoAcceptance | None = None
    purchase_order_ref: Annotated[str | None, Field(max_length=255, min_length=1)] = None
    governance_context: Annotated[str | None, Field(max_length=4096, min_length=1)] = None
    push_notification_config: push_notification_config_1.PushNotificationConfig | None = None
    reporting_webhook: Annotated[
        reporting_webhook_1.ReportingWebhook | None,
        Field(
            description='Optional reporting delivery configuration established atomically when the proposal is accepted. This is execution metadata and does not alter the accepted commercial terms digest.'
        ),
    ] = None
    opportunity: Annotated[
        Opportunity | None,
        Field(
            description='Optional planning-cycle closure. Success infers closed with accepted_with_seller when status is omitted. If the proposal carries opportunity_id, a supplied ID MUST match; the accepted proposal preserves that association.'
        ),
    ] = None
    context: context_1.ContextObject | None = None
    ext: ext_1.ExtensionObject | None = None

The request message of a task in the pinned bundle's task registry.

A consumer holding one can resolve its account, decide at-most-once, echo its context and negotiate version – the whole transport-boundary job – before knowing which tool it is. issubclass(model, AdcpRequest) is the registration-time proof that a model is spec-derived rather than a hand-written parallel: a field test passes for a forged model, descent does not.

Each accessor returns the field's value, or None when this tool's schema declares no such field. Only 49 of the 87 request schemas declare an account and only 43 an idempotency_key, so asking the request is what replaces getattr(req, "account", None) against Any at the boundary.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var account : CanonicalAccountReference1 | CanonicalAccountReference2
var budget_cap_timezone : str | None
var context : ContextObject | None
var daily_budget_cap : float | None
var ext : ExtensionObject | None
var governance_context : str | None
var idempotency_key : str
var io_acceptance : IoAcceptance | None
var model_config
var name : str | None
var opportunity : Opportunity | None
var proposal_id : str
var proposal_terms_digest : str
var purchase_order_ref : str | None
var push_notification_config : PushNotificationConfig | None
var reporting_webhook : ReportingWebhook | None
var total_budget : TotalBudget | None

Instance variables

var adcp_major_version : int | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.

Inherited members

class AcceptanceContext (**data: Any)
Expand source code
class AcceptanceContext(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    subjects: Annotated[list[Subject] | None, Field(min_length=1)] = None
    advertiser_roles: Annotated[list[AdvertiserRole] | None, Field(min_length=1)] = None
    advertiser_industry: advertiser_industry_1.AdvertiserIndustry | None = None
    advertiser_jurisdictions: Annotated[
        list[AdvertiserJurisdiction] | None,
        Field(
            description='Jurisdictions in which the advertiser is established or legally organized. This is distinct from where an ad will be delivered.',
            min_length=1,
        ),
    ] = None
    delivery_jurisdictions: Annotated[
        list[DeliveryJurisdiction] | None,
        Field(
            description="Jurisdictions in which the proposed advertising will be delivered. Seller acceptance rules' jurisdictions and jurisdiction_groups match this field.",
            min_length=1,
        ),
    ] = None
    ext: ext_1.ExtensionObject | None = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var advertiser_industry : AdvertiserIndustry | None
var advertiser_jurisdictions : list[AdvertiserJurisdiction] | None
var advertiser_roles : list[AdvertiserRole] | None
var delivery_jurisdictions : list[DeliveryJurisdiction] | None
var ext : ExtensionObject | None
var model_config
var subjects : list[Subject] | None

Inherited members

class AcceptancePolicyCatalog (**data: Any)
Expand source code
class AcceptancePolicyCatalog(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    catalog_version: Annotated[str, Field(min_length=1)]
    generated_at: AwareDatetime | None = None
    profiles: Annotated[
        list[acceptance_policy_profile.AcceptancePolicyProfile] | None, Field(min_length=1)
    ] = None
    registry_profiles: Annotated[
        list[acceptance_policy_profile_ref.RegistryAcceptancePolicyProfileReference] | None,
        Field(
            description='Exact reusable profiles adopted from the shared policy registry. Resolution failure is unknown, never allowed. A seller adds a distinct local profile to narrow a registry profile.',
            min_length=1,
        ),
    ] = None
    ext: ext_1.ExtensionObject | None = None

    @model_validator(mode='after')
    def _require_schema_required_group(self) -> AcceptancePolicyCatalog:
        # ``required`` asks whether the caller supplied the field, which is what
        # model_fields_set answers. An explicit null is a supplied value — on a
        # mutation input it is the command to clear — and a default the caller
        # never sent is not.
        for group in (('profiles',), ('registry_profiles',),):
            if all(name in self.model_fields_set for name in group):
                return self
        raise ValueError(
            'AcceptancePolicyCatalog requires at least one of these field groups: profiles | registry_profiles'
        )

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var catalog_version : str
var ext : ExtensionObject | None
var generated_at : pydantic.types.AwareDatetime | None
var model_config
var profiles : list[AcceptancePolicyProfile] | None
var registry_profiles : list[RegistryAcceptancePolicyProfileReference] | None

Inherited members

class AcceptancePolicyDiscovery (**data: Any)
Expand source code
class AcceptancePolicyDiscovery(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    catalog_url: Annotated[
        AnyUrl,
        Field(description='HTTPS document that validates against acceptance-policy-catalog.json.'),
    ]
    catalog_digest: Annotated[
        str,
        Field(
            description='SHA-256 digest of the exact catalog representation fetched from catalog_url.',
            pattern='^sha256:[a-f0-9]{64}$',
        ),
    ]
    default_profile_ids: Annotated[
        list[DefaultProfileId] | None,
        Field(
            description='Local or registry-referenced catalog profiles that apply seller-wide unless a product adds further profiles. IDs MUST resolve uniquely across profiles and registry_profiles; all referenced profiles compose restrictively.',
            min_length=1,
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var catalog_digest : str
var catalog_url : pydantic.networks.AnyUrl
var default_profile_ids : list[DefaultProfileId] | None
var model_config

Inherited members

class AcceptancePolicyProfile (**data: Any)
Expand source code
class AcceptancePolicyProfile(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    profile_id: Annotated[str, Field(pattern='^[A-Za-z0-9_.:-]+$')]
    version: Annotated[str, Field(min_length=1)]
    content_digest: Annotated[
        str,
        Field(
            description='SHA-256 digest of the RFC 8785 JCS serialization of this profile with content_digest omitted. A profile_id/version pair is immutable; consumers reject a resolved profile whose digest differs.',
            pattern='^sha256:[a-f0-9]{64}$',
        ),
    ]
    policy_refs: Annotated[
        list[PolicyRef],
        Field(
            description='Exact registry policy versions from which this profile was derived. Consumers MUST NOT silently substitute a different version.',
            min_length=1,
        ),
    ]
    coverage: Annotated[
        Coverage,
        Field(
            description='partial means additional unpublished rules may apply and omission is unknown. complete means this profile is exhaustive only for its declared scope and version.'
        ),
    ]
    scope: Annotated[
        Scope | None,
        Field(
            description='The boundary within which a complete profile claims exhaustiveness. It is informative for partial profiles and mandatory for complete profiles.'
        ),
    ] = None
    region_aliases: Annotated[
        dict[Annotated[str, StringConstraints(pattern=r'^[A-Z][A-Z0-9_-]*$')], list[RegionAliase]] | None,
        Field(
            description='Profile-local named country groups. Rules may reference only keys declared here; consumers expand them before matching.'
        ),
    ] = None
    description: Annotated[str | None, Field(min_length=1)] = None
    rules: Annotated[list[acceptance_policy_rule.AcceptancePolicyRule], Field(min_length=1)]
    ext: ext_1.ExtensionObject | None = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var content_digest : str
var coverage : Coverage
var description : str | None
var ext : ExtensionObject | None
var model_config
var policy_refs : list[PolicyRef]
var profile_id : str
var region_aliases : dict[str, list[RegionAliase]] | None
var rules : list[AcceptancePolicyRule]
var scope : Scope | None
var version : str

Inherited members

class AcceptancePolicyProfileId (value: Any = <object object>, *, root: Any = <object object>)
Expand source code
class AcceptancePolicyProfileId(ScalarStr):
    __slots__ = ()
    _constraints = {'min_length': 1}

A str generated from a JSON Schema string root.

Ancestors

  • adcp.types._scalar.ScalarStr
  • adcp.types._scalar._ScalarRoot
  • builtins.str
class AcceptancePolicyProfileIds (root: RootModelRootType = PydanticUndefined, **data)
Expand source code
class AcceptancePolicyProfileIds(RootModel[list[AcceptancePolicyProfileId]]):
    root: Annotated[
        list[AcceptancePolicyProfileId],
        Field(
            description='Acceptance-policy profiles from the seller catalog that apply to this product in addition to seller defaults. Profiles compose restrictively; the most restrictive matching disposition wins.',
            min_length=1,
            title='Acceptance Policy Profile IDs',
        ),
    ]

Usage Documentation

RootModel and Custom Root Types

A Pydantic BaseModel for the root object of the model.

Attributes
-----=
root
The root object of the model.
__pydantic_root_model__
Whether the model is a RootModel.
__pydantic_private__
Private fields in the model.
__pydantic_extra__
Extra fields in the model.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

  • pydantic.root_model.RootModel[list[AcceptancePolicyProfileId]]
  • pydantic.root_model.RootModel
  • pydantic.main.BaseModel
  • typing.Generic

Class variables

var model_config
var root : list[AcceptancePolicyProfileId]
class AcceptancePolicyRule (**data: Any)
Expand source code
class AcceptancePolicyRule(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    rule_id: Annotated[str, Field(pattern='^[A-Za-z0-9_.:-]+$')]
    subject_category: Annotated[
        str,
        Field(
            description='Registry policy-category-definition category_id. Named subject_category to avoid collision with PolicyEntry.category, whose values are regulation and standard.',
            pattern='^[a-z][a-z0-9_]*$',
        ),
    ]
    subject_facets: Annotated[
        list[SubjectFacet] | None,
        Field(
            description='Facet IDs defined by the selected policy category. Omission means the rule applies to every facet in the category.',
            min_length=1,
        ),
    ] = None
    advertiser_roles: Annotated[
        list[AdvertiserRole] | None,
        Field(
            description='Registry-extensible roles such as political_actor, election_authority, government_entity, news_publisher, or commercial_advertiser.',
            min_length=1,
        ),
    ] = None
    jurisdictions: Annotated[
        list[Jurisdiction] | None,
        Field(
            description='Delivery jurisdictions where this rule applies. Omission means every jurisdiction served by the seller.',
            min_length=1,
        ),
    ] = None
    jurisdiction_groups: Annotated[
        list[JurisdictionGroup] | None,
        Field(
            description="Named country groups declared by the containing profile's region_aliases. Unknown group IDs invalidate the profile; they never match permissively.",
            min_length=1,
        ),
    ] = None
    applies_to: Annotated[list[AppliesToEnum], Field(min_length=1)]
    disposition: Disposition
    requirements: Annotated[
        list[acceptance_policy_requirement.AcceptancePolicyRequirement] | None, Field(min_length=1)
    ] = None
    policy_ids: Annotated[
        list[PolicyId] | None,
        Field(
            description='Registry policies that define the exact obligations behind this coarse rule.',
            min_length=1,
        ),
    ] = None
    description: Annotated[
        str | None,
        Field(
            description='Display-only explanation. Matchers MUST NOT interpret this text as executable instructions or use it to override typed fields.',
            max_length=1000,
            min_length=1,
        ),
    ] = None
    effective_at: AwareDatetime | None = None
    expires_at: AwareDatetime | None = None
    ext: ext_1.ExtensionObject | None = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var advertiser_roles : list[AdvertiserRole] | None
var applies_to : list[AppliesToEnum]
var description : str | None
var disposition : Disposition
var effective_at : pydantic.types.AwareDatetime | None
var expires_at : pydantic.types.AwareDatetime | None
var ext : ExtensionObject | None
var jurisdiction_groups : list[JurisdictionGroup] | None
var jurisdictions : list[Jurisdiction] | None
var model_config
var policy_ids : list[PolicyId] | None
var requirements : list[AcceptancePolicyRequirement1 | AcceptancePolicyRequirement2 | AcceptancePolicyRequirement3 | AcceptancePolicyRequirement4 | AcceptancePolicyRequirement5 | AcceptancePolicyRequirement6 | AcceptancePolicyRequirement7 | AcceptancePolicyRequirement8 | AcceptancePolicyRequirement9 | AcceptancePolicyRequirement10 | AcceptancePolicyRequirement11 | AcceptancePolicyRequirement12 | AcceptancePolicyRequirement13 | AcceptancePolicyRequirement14 | AcceptancePolicyRequirement15 | AcceptancePolicyRequirement16 | AcceptancePolicyRequirement17] | None
var rule_id : str
var subject_category : str
var subject_facets : list[SubjectFacet] | None

Inherited members

class AcceptedLoss (*args, **kwds)
Expand source code
class AcceptedLoss(StrEnum):
    feed_version_not_atomic = 'feed_version_not_atomic'
    pricing_version_not_atomic = 'pricing_version_not_atomic'
    mutation_idempotency_not_guaranteed = 'mutation_idempotency_not_guaranteed'

Enum where members are also (and must be) strings

Ancestors

  • enum.StrEnum
  • builtins.str
  • enum.ReprEnum
  • enum.Enum

Class variables

var feed_version_not_atomic
var mutation_idempotency_not_guaranteed
var pricing_version_not_atomic
class AggregatedTotals (**data: Any)
Expand source code
class AggregatedTotals(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    impressions: Annotated[
        StrictFloat, Field(description='Total impressions delivered across all media buys', ge=0.0)
    ]
    spend: Annotated[
        StrictFloat, Field(description='Total amount spent across all media buys', ge=0.0)
    ]
    clicks: Annotated[
        StrictFloat | None,
        Field(description='Total clicks across all media buys (if applicable)', ge=0.0),
    ] = None
    completed_views: Annotated[
        StrictFloat | None,
        Field(
            description='Total audio/video completions across all media buys (if applicable)',
            ge=0.0,
        ),
    ] = None
    views: Annotated[
        StrictFloat | None,
        Field(description='Total views across all media buys (if applicable)', ge=0.0),
    ] = None
    conversions: Annotated[
        StrictFloat | None,
        Field(description='Total conversions across all media buys (if applicable)', ge=0.0),
    ] = None
    conversion_value: Annotated[
        StrictFloat | None,
        Field(description='Total conversion value across all media buys (if applicable)', ge=0.0),
    ] = None
    commissionable_value: Annotated[
        StrictFloat | None,
        Field(
            description='Total settled conversion value eligible for revenue-share commission across all media buys (if applicable)',
            ge=0.0,
        ),
    ] = None
    roas: Annotated[
        StrictFloat | None,
        Field(
            description='Aggregate return on ad spend across all media buys (total conversion_value / total spend)',
            ge=0.0,
        ),
    ] = None
    new_to_brand_rate: Annotated[
        StrictFloat | None,
        Field(
            description='Fraction of total conversions across all media buys from first-time brand buyers (weighted by conversion volume, not a simple average of per-buy rates)',
            ge=0.0,
            le=1.0,
        ),
    ] = None
    cost_per_acquisition: Annotated[
        StrictFloat | None,
        Field(
            description='Aggregate cost per conversion across all media buys (total spend / total conversions)',
            ge=0.0,
        ),
    ] = None
    completion_rate: Annotated[
        StrictFloat | None,
        Field(
            description='Aggregate completion rate across all media buys (weighted by impressions, not a simple average of per-buy rates). Null indicates the metric is not applicable to the aggregated buys (e.g. all non-video inventory).',
            ge=0.0,
            le=1.0,
        ),
    ] = None
    reach: Annotated[
        StrictFloat | None,
        Field(
            description='Reach across all media buys. Only present when all media buys share the same reach_unit. Omitted when reach units are heterogeneous — use per-buy reach values instead. The optional reach_aggregation field declares whether this value is deduplicated across buys or is a sum of constituent reach values.',
            ge=0.0,
        ),
    ] = None
    reach_aggregation: Annotated[
        reach_aggregation_1.ReachAggregation | None,
        Field(
            description='How reach was combined across the media buys in this aggregate. When omitted, legacy reach semantics are unknown and consumers MUST NOT use reach as the denominator for frequency.'
        ),
    ] = None
    reach_unit: Annotated[
        reach_unit_1.ReachUnit | None,
        Field(
            description='Unit of measurement for reach. Only present when all aggregated media buys use the same reach_unit.'
        ),
    ] = None
    frequency: Annotated[
        StrictFloat | None,
        Field(
            description='Average frequency per reach unit across all media buys (impressions / reach). In new payloads, only present when reach is present and reach_aggregation is deduplicated. MUST be omitted when reach_aggregation is sum_of_constituent_reach. Legacy payloads that omit reach_aggregation remain schema-valid, but consumers MUST NOT treat their reach as a safe frequency denominator.',
            ge=0.0,
        ),
    ] = None
    media_buy_count: Annotated[
        SchemaInt, Field(description='Number of media buys included in the response', ge=0)
    ]
    metric_aggregates: Annotated[
        list[delivery_metric_aggregate.DeliveryMetricAggregate] | None,
        Field(
            description="Cross-buy delivery aggregates partitioned by qualifier. Row-symmetric with `package.committed_metrics` and `by_package[].missing_metrics` — same atomic unit `(scope, metric_id, qualifier)` — so reconciliation collapses to a row-level join on the tuple. Granularity rule: one row per `(metric_id, full-qualifier-set)`, reported at the finest available granularity; buyers re-aggregate up if they want a coarser view. Used only for metrics with non-empty qualifier sets — unqualified metrics (`impressions`, `spend`, `media_buy_count`, etc.) remain at the top of `aggregated_totals`. **Mutual exclusion MUST**: for any `metric_id` appearing in `metric_aggregates`, the corresponding top-level scalar in `aggregated_totals` MUST be omitted (not zeroed) — avoids duplicate sources of truth. The qualifier vocabulary on this delivery surface is closed today (`additionalProperties: false`, same content as `committed_metrics.qualifier`) but is expected to **diverge from contract qualifier in future minors** as transparency disclosures buyers don't commit to ship delivery-only (e.g., `tracker_firing` pending #3832 resolution). Each row carries a `value` plus inlined per-metric component fields (e.g., `measurable_impressions` and `viewable_impressions` for `viewable_rate`; `spend` and `conversions` for `cost_per_acquisition`). Per-buy `totals` keeps its flat shape — each buy is single-qualifier by definition; only the aggregate spans qualifiers. **Qualifier-set drift across reports**: when a campaign gains a new qualifier mid-flight (e.g., adds `tracker_firing` partitioning in week 2), prior periods' rows remain valid at their original granularity; buyers SHOULD NOT retroactively repartition.",
            examples=[
                [
                    {
                        'scope': 'standard',
                        'metric_id': 'viewable_rate',
                        'qualifier': {'viewability_standard': 'mrc'},
                        'value': 0.7286,
                        'measurable_impressions': 700000,
                        'viewable_impressions': 510000,
                    },
                    {
                        'scope': 'standard',
                        'metric_id': 'viewable_rate',
                        'qualifier': {'viewability_standard': 'groupm'},
                        'value': 0.55,
                        'measurable_impressions': 180000,
                        'viewable_impressions': 99000,
                    },
                    {
                        'scope': 'vendor',
                        'vendor': {'domain': 'attentionvendor.example'},
                        'metric_id': 'attention_units',
                        'qualifier': {},
                        'value': 4.2,
                        'measurable_impressions': 800000,
                    },
                ]
            ],
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var clicks : float | None
var commissionable_value : float | None
var completed_views : float | None
var completion_rate : float | None
var conversion_value : float | None
var conversions : float | None
var cost_per_acquisition : float | None
var frequency : float | None
var impressions : float
var media_buy_count : int
var metric_aggregates : list[DeliveryMetricAggregate1 | DeliveryMetricAggregate2] | None
var model_config
var new_to_brand_rate : float | None
var reach : float | None
var reach_aggregation : ReachAggregation | None
var reach_unit : ReachUnit | None
var roas : float | None
var spend : float
var views : float | None

Inherited members

class AssignedPackage (**data: Any)
Expand source code
class AssignedPackage(IndicatorBearingResourceState):
    model_config = ConfigDict(
        extra='allow',
    )
    indicator_types_evaluated: Annotated[
        list[IndicatorTypesEvaluatedEnum] | None,
        Field(
            description='Indicator types covered by this snapshot. Required whenever indicators is present. Types omitted from this list remain unknown even when indicators is empty. Every returned indicator.type MUST appear in this list.',
            min_length=1,
        ),
    ] = None
    indicators: Annotated[
        list[Indicator] | None,
        Field(
            description='Current seller assertions for the indicator types and publisher/placement coverage named by the sibling evaluation fields. Omitted means unknown or not evaluated. A present empty array means evaluated with no current assertion for indicator_types_evaluated in the evaluated scope.'
        ),
    ] = None
    package_id: Annotated[str, Field(description='Package identifier')]
    media_buy_id: Annotated[
        str | None,
        Field(
            description='Media buy containing this package. A seller advertising list_creatives in media_buy.relationship_notifications.projection_tasks MUST include this field on every assignment row, including rows where indicators is omitted as unknown, so buyers can key and reread the relationship unambiguously when package IDs are reused across media buys.'
        ),
    ] = None
    assigned_date: Annotated[AwareDatetime, Field(description='When this assignment was created')]
    approval_status: Annotated[
        creative_approval_status.CreativeApprovalStatus | None,
        Field(
            description='Aggregate approval state for this creative in this package assignment. This mirrors the same relationship in get_media_buys. Sellers advertising list_creatives as an indicator projection task MUST include it; partially_approved requires approval_scopes.'
        ),
    ] = None
    rejection_reason: Annotated[
        str | None,
        Field(
            description='Human-readable explanation when approval_status is rejected. Mirrors get_media_buys for the same relationship.'
        ),
    ] = None
    approval_scopes: Annotated[
        list[creative_approval_scope.ScopedCreativeApproval] | None,
        Field(
            description='Complete, disjoint publisher/placement approval partition when approval_status is partially_approved. A normalized scope appears once. For one publisher, use either one publisher-wide row or placement-specific rows, never both. Omit when one approval_status applies uniformly to the whole assignment. The same scoped outcomes are mirrored on get_media_buys.',
            min_length=2,
        ),
    ] = None
    indicators_as_of: Annotated[
        AwareDatetime | None,
        Field(
            description='When the seller last completed the evaluation represented by indicators for this relationship. Required whenever indicators is present, including an empty array.'
        ),
    ] = None
    indicators_evaluated_scope: Annotated[
        list[indicator_scope.IndicatorScope] | None,
        Field(
            description='Optional publisher or placement scopes covered by this evaluation. Omit when indicators covers the whole package–creative assignment. When present, scopes not listed remain unknown; every returned indicator.scope entry MUST be contained by this set.',
            min_length=1,
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var approval_scopes : list[ScopedCreativeApproval] | None
var approval_status : CreativeApprovalStatus | None
var assigned_date : pydantic.types.AwareDatetime
var indicator_types_evaluated : list[IndicatorTypesEvaluatedEnum] | None
var indicators : list[Indicator] | None
var indicators_as_of : pydantic.types.AwareDatetime | None
var indicators_evaluated_scope : list[IndicatorScope] | None
var media_buy_id : str | None
var model_config
var package_id : str
var rejection_reason : str | None

Inherited members

class BuyProductsRequest (**data: Any)
Expand source code
class BuyProductsRequest(AdcpRequest, AdcpVersionEnvelope):
    model_config = ConfigDict(
        extra='forbid',
    )
    idempotency_key: Annotated[
        str, Field(max_length=255, min_length=16, pattern='^[A-Za-z0-9_.:-]{16,255}$')
    ]
    name: Annotated[
        str | None,
        Field(
            description='Human-readable name for this media buy, shared by buyer and seller for trafficking UI display and operational communication. When supplied, the seller MUST persist it and echo it unchanged on the commitment success response and subsequent get_media_buys reads. The name is operational metadata outside accepted_proposal and is not covered by terms_digest. This display label is not an identifier or financial reference.',
            max_length=255,
            min_length=1,
            pattern='\\S',
        ),
    ] = None
    account: Annotated[
        canonical_account_ref.CanonicalAccountReference,
        Field(
            description='Execution account. A natural-key account is the single brand source and MUST NOT be combined with top-level brand.'
        ),
    ]
    brand: Annotated[
        brand_key.BrandKey | None,
        Field(
            description='Brand source required when account is ID-only. Omit when account already contains brand and operator.'
        ),
    ] = None
    advertiser_industry: Annotated[
        advertiser_industry_1.AdvertiserIndustry | None,
        Field(
            description='Industry classification for this campaign. Sellers may infer it from the resolved brand manifest when omitted.'
        ),
    ] = None
    feed_version: Annotated[
        str,
        Field(
            description='list_products feed_version containing the published offers being accepted. Sellers reject stale or mismatched versions rather than silently applying changed terms.',
            min_length=1,
        ),
    ]
    pricing_version: Annotated[
        str | None,
        Field(
            description='list_products pricing_version containing the accepted rate. Buyers MUST include this whenever list_products returned one; omission means the seller does not version pricing separately.',
            min_length=1,
        ),
    ] = None
    purchases: Annotated[list[product_purchase_input.ProductPurchaseInput], Field(min_length=1)]
    total_budget: TotalBudget | None = None
    daily_budget_cap: Annotated[
        StrictFloat | None,
        Field(
            description="Optional hard aggregate daily spend ceiling in total_budget.currency or the media buy's derived currency. It bounds the shared daily spend pool without allocating or reserving amounts for purchases.",
            ge=0.0,
        ),
    ] = None
    frequency_cap: Annotated[
        media_buy_frequency_cap.MediaBuyFrequencyCap | None,
        Field(
            description='Optional max-impression cap using one counter across purchases. Buyers send it only when aggregate_frequency_capping is advertised. Every selected product must declare compatible media_buy_support; otherwise the purchase is rejected atomically with UNSUPPORTED_FEATURE before any mutation, never clamped.'
        ),
    ] = None
    budget_cap_timezone: Annotated[
        str | None,
        Field(
            description='Optional IANA timezone override shared by every aggregate and purchase daily cap. Requires buyer_timezone_override support; otherwise rejected with UNSUPPORTED_FEATURE. When omitted, budget_capping.timezone_basis selects Account.timezone or fixed_timezone.',
            min_length=1,
        ),
    ] = None
    budget_allocation: canonical_budget_allocation.CanonicalBudgetAllocation | None = None
    start_time: start_timing.StartTiming
    end_time: AwareDatetime
    pacing: Annotated[
        pacing_1.Pacing | None,
        Field(
            description='Aggregate media-buy pacing. In seller-optimized allocation, a seller declaring media_buy.features.seller_optimized_budget MUST accept omission and `even`; it MAY reject `asap` or `front_loaded` with UNSUPPORTED_FEATURE (error.field `pacing`) before any provider mutation and MUST NOT silently coerce them to `even`. Fixed-allocation semantics are unchanged.'
        ),
    ] = None
    bidding: bidding_policy.BiddingPolicy | None = None
    paused: StrictBool | None = False
    purchase_order_ref: Annotated[str | None, Field(max_length=255, min_length=1)] = None
    agency_estimate_number: Annotated[str | None, Field(max_length=100)] = None
    invoice_recipient: Annotated[
        business_entity.BusinessEntity | None,
        Field(description='Authorized per-buy billing entity override.'),
    ] = None
    governance_context: Annotated[str | None, Field(max_length=4096, min_length=1)] = None
    push_notification_config: push_notification_config_1.PushNotificationConfig | None = None
    reporting_webhook: Annotated[
        reporting_webhook_1.ReportingWebhook | None,
        Field(
            description='Optional reporting delivery configuration established atomically with the MediaBuy. This is execution metadata and is not part of the immutable product pricing terms.'
        ),
    ] = None
    opportunity: Annotated[
        Opportunity | None,
        Field(
            description='Optional planning-cycle closure for a direct product purchase. Success infers closed with accepted_with_seller when status is omitted; an explicit status MUST carry that same closure.'
        ),
    ] = None
    context: context_1.ContextObject | None = None
    ext: ext_1.ExtensionObject | None = None

The request message of a task in the pinned bundle's task registry.

A consumer holding one can resolve its account, decide at-most-once, echo its context and negotiate version – the whole transport-boundary job – before knowing which tool it is. issubclass(model, AdcpRequest) is the registration-time proof that a model is spec-derived rather than a hand-written parallel: a field test passes for a forged model, descent does not.

Each accessor returns the field's value, or None when this tool's schema declares no such field. Only 49 of the 87 request schemas declare an account and only 43 an idempotency_key, so asking the request is what replaces getattr(req, "account", None) against Any at the boundary.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var account : CanonicalAccountReference1 | CanonicalAccountReference2
var advertiser_industry : AdvertiserIndustry | None
var agency_estimate_number : str | None
var bidding : BiddingPolicy | None
var brand : BrandKey | None
var budget_allocation : CanonicalBudgetAllocation1 | CanonicalBudgetAllocation2 | None
var budget_cap_timezone : str | None
var context : ContextObject | None
var daily_budget_cap : float | None
var end_time : pydantic.types.AwareDatetime
var ext : ExtensionObject | None
var feed_version : str
var frequency_cap : MediaBuyFrequencyCap | None
var governance_context : str | None
var idempotency_key : str
var invoice_recipient : BusinessEntity | None
var model_config
var name : str | None
var opportunity : Opportunity | None
var pacing : Pacing | None
var paused : bool | None
var pricing_version : str | None
var purchase_order_ref : str | None
var purchases : list[ProductPurchaseInput]
var push_notification_config : PushNotificationConfig | None
var reporting_webhook : ReportingWebhook | None
var start_time : Literal['asap'] | pydantic.types.AwareDatetime
var total_budget : TotalBudget | None

Instance variables

var adcp_major_version : int | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.

Inherited members

class ByPackageItem (**data: Any)
Expand source code
class ByPackageItem(DeliveryMetrics):
    package_id: Annotated[str, Field(description="Seller's package identifier")]
    pacing_index: Annotated[
        StrictFloat | None,
        Field(
            description='Package delivery pace relative to its package-level pacing plan (1.0 = on track, <1.0 = behind, >1.0 = ahead). In seller-optimized mode this is a subordinate diagnostic and may be absent when no package pacing preference exists.',
            ge=0.0,
        ),
    ] = None
    pricing_model: Annotated[
        pricing_model_1.PricingModel,
        Field(
            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: Annotated[
        StrictFloat,
        Field(
            description='The pricing rate for this package. For fixed-rate pricing, this is the agreed currency-denominated unit rate (e.g., CPM rate of 12.50 means $12.50 per 1,000 impressions). For auction-based pricing, this is the effective rate based on actual delivery. For revenue_share, this is the decimal commission rate (e.g., 0.04 means 4%) and is not itself currency-denominated.',
            ge=0.0,
        ),
    ]
    currency: Annotated[
        str,
        Field(
            description="ISO 4217 currency code for this package's spend and currency-denominated pricing rate. The rate for revenue_share is a dimensionless commission fraction, but attributed monetary values still use this currency. When the enclosing media_buy_deliveries[].currency is present, this value MUST equal it. For AdCP-authored buys both values MUST equal the media-buy currency. A different package currency is permitted only for a legacy or externally created mixed-currency buy whose row currency, daily_breakdown, and row/window monetary totals are omitted. AdCP does not perform currency conversion.",
            pattern='^[A-Z]{3}$',
        ),
    ]
    delivery_status: Annotated[
        delivery_status_1.DeliveryStatus | None,
        Field(
            description="System-reported operational state of this package. Reflects actual delivery state independent of buyer pause control. 'not_delivering' means zero impressions were recorded for the entire reporting_period while the package was in-flight. Sellers SHOULD only report 'not_delivering' once the package's data is_final for the period — a provisional (is_final: false) zero may still be measurement catching up, not genuine non-delivery."
        ),
    ] = None
    paused: Annotated[
        StrictBool | None,
        Field(description='Whether this package is currently paused by the buyer'),
    ] = None
    is_final: Annotated[
        StrictBool | None,
        Field(
            description="Whether this delivery data is final for the reporting period. When false, the data may be updated as measurement matures (e.g., broadcast C7 window accumulating DVR playback) or as processing completes (e.g., IVT filtering, deduplication). When true, the seller considers this data closed — no further updates for this period — and is willing to invoice on it subject to the buy's `measurement_terms.billing_measurement`. Absent means the seller does not distinguish provisional from final data."
        ),
    ] = None
    finalized_at: Annotated[
        AwareDatetime | None,
        Field(
            description="ISO 8601 timestamp at which this package's data became final. Present only when `is_final: true`. Anchors reconciliation and (when later defined) dispute-window clocks against the buy's `measurement_terms.billing_measurement.measurement_window`."
        ),
    ] = None
    measurement_window: Annotated[
        str | None,
        Field(
            description="Which measurement window this data represents, referencing a window_id from the product's reporting_capabilities.measurement_windows. For broadcast: 'live', 'c3', 'c7'. When absent, the data is not windowed (standard digital reporting). When present with is_final: false, a later report for the same period will provide a wider window or more complete data.",
            examples=['live', 'c3', 'c7'],
            max_length=50,
        ),
    ] = None
    supersedes_window: Annotated[
        str | None,
        Field(
            description="Which measurement window this data replaces. Present on window_update notifications to indicate progression (e.g., 'live' when reporting C3 data that supersedes live-only numbers). Absent on the first report for a period. Buyers should replace stored data for the superseded window with this report's data.",
            examples=['live', 'c3'],
            max_length=50,
        ),
    ] = None
    missing_metrics: Annotated[
        list[missing_metric.MissingMetric] | None,
        Field(
            description="Metrics that the binding reporting contract declared but that are NOT populated in this report. Reconciliation source: when `package.committed_metrics` is present, `missing_metrics` is computed against entries where `committed_at < reporting_period.end` — independent of subsequent product mutations and respecting the commitment timestamp on each entry (a metric committed mid-flight is only flagged missing in reports for periods after its commitment). When `package.committed_metrics` is absent, fall back to the product's current `reporting_capabilities.available_metrics` (no timestamp filter). Empty array (or absent) indicates clean delivery against the contract. Non-empty signals an accountability breach — the seller committed to the metric but did not produce the value here. Sellers MUST exclude metrics that are not yet measurable for the current `measurement_window` (e.g., post-IVT counts during the live window) — those will appear (or not) when a wider window supersedes this report via `supersedes_window`. Each entry uses an explicit `scope` discriminator: `standard` for entries from the closed `available-metric.json` enum, `vendor` for vendor-defined metrics anchored on a BrandRef. Symmetric with `committed_metrics`. When the request narrowed the payload via requested_metrics, sellers MUST NOT list a committed metric here solely because the buyer excluded it — missing_metrics reports delivery gaps, not request narrowing.",
            examples=[
                [],
                [{'scope': 'standard', 'metric_id': 'completed_views'}],
                [
                    {'scope': 'standard', 'metric_id': 'completed_views'},
                    {
                        'scope': 'vendor',
                        'vendor': {'domain': 'attentionvendor.example'},
                        'metric_id': 'attention_units',
                    },
                ],
            ],
        ),
    ] = None
    metric_values: Annotated[
        list[package_delivery_metric_value.PackageDeliveryMetricValue] | None,
        Field(
            description='Qualified standard delivery values for this package. Each entry is the delivered counterpart to a standard-scope package.committed_metrics or by_package[].missing_metrics row, using the same atomic key (scope, metric_id, qualifier). Sellers report one row per full qualifier set at package grain; when a metric appears here, its flat scalar counterpart on the package MUST be omitted to avoid two sources of truth. Vendor-scope values continue to use vendor_metric_values. Buyers reconcile rows directly and perform any compatible cross-package aggregation themselves.'
        ),
    ] = None
    by_catalog_item: Annotated[
        list[catalog_item_delivery_metrics.CatalogItemDeliveryMetrics] | None,
        Field(
            description='Delivery by catalog item within this package. Available for catalog-driven packages when the seller supports item-level reporting.'
        ),
    ] = None
    by_catalog_item_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether by_catalog_item was truncated due to the requested limit or a seller-imposed maximum. Sellers MUST return this flag whenever by_catalog_item is present and the request included reporting_dimensions.catalog_item (false means the list is complete). When the breakdown was returned automatically without a request key, the flag is RECOMMENDED but not required — automatic rows carry no completeness contract.'
        ),
    ] = None
    by_catalog_item_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description="The metric actually used to order by_catalog_item rows. Sellers MUST return this field whenever by_catalog_item is present and the request included reporting_dimensions.catalog_item. When the seller cannot sort by the requested sort_by metric it falls back to 'spend'; this echo makes the fallback visible instead of silently returning rows the buyer will misread as ordered by the requested metric. When the breakdown was returned automatically without a request key, the field is RECOMMENDED but not required — automatic rows carry no completeness contract."
        ),
    ] = None
    by_catalog_item_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_catalog_item ordering. Sellers MUST return this field whenever by_catalog_item is present and the request included reporting_dimensions.catalog_item, alongside by_catalog_item_sorted_by. When the breakdown was returned automatically without a request key, the field is RECOMMENDED but not required — automatic rows carry no completeness contract.'
        ),
    ] = None
    by_creative: Annotated[
        list[creative_delivery_metrics.CreativeDeliveryMetrics] | None,
        Field(
            description='Metrics broken down by creative within this package. Available when the seller supports creative-level reporting.'
        ),
    ] = None
    by_format: Annotated[
        list[ByFormatItem] | None,
        Field(
            description="Delivery by canonical creative format kind within this package. Negotiated on the GET path when the buyer requests reporting_dimensions.format and the product declares supports_format_breakdown; reporting webhook configuration does not negotiate or guarantee this breakdown. Each row aggregates every served creative of that format kind. Sellers MUST aggregate all delivery using adopter-defined shapes into one format_kind 'custom' row. When by_format_truncated is false, additive metrics such as impressions and spend across the rows SHOULD reconcile to the corresponding package totals, subject to the measurement and attribution semantics of each metric. Buyers MUST NOT expect row-level correspondence between by_format and by_creative because the two breakdowns are independently produced at different grains."
        ),
    ] = None
    by_format_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether by_format was truncated due to the requested limit or a seller-imposed maximum. Sellers MUST return this flag whenever by_format is present (false means the list is complete).'
        ),
    ] = None
    by_format_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description="The metric actually used to order by_format rows. Sellers MUST return this field whenever by_format is present. When the seller cannot sort by the requested sort_by metric it falls back to 'spend'; this echo makes the fallback visible instead of silently returning rows the buyer will misread as ordered by the requested metric."
        ),
    ] = None
    by_format_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_format ordering. Sellers MUST return this field whenever by_format is present.'
        ),
    ] = None
    by_creative_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether by_creative was truncated due to the requested limit or a seller-imposed maximum. Sellers MUST return this flag whenever by_creative is present and the request included reporting_dimensions.creative (false means the list is complete). When the breakdown was returned automatically without a request key, the flag is RECOMMENDED but not required — automatic rows carry no completeness contract.'
        ),
    ] = None
    by_creative_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description="The metric actually used to order by_creative rows. Sellers MUST return this field whenever by_creative is present and the request included reporting_dimensions.creative. When the seller cannot sort by the requested sort_by metric it falls back to 'spend'; this echo makes the fallback visible instead of silently returning rows the buyer will misread as ordered by the requested metric. When the breakdown was returned automatically without a request key, the field is RECOMMENDED but not required — automatic rows carry no completeness contract."
        ),
    ] = None
    by_creative_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_creative ordering. Sellers MUST return this field whenever by_creative is present and the request included reporting_dimensions.creative, alongside by_creative_sorted_by. When the breakdown was returned automatically without a request key, the field is RECOMMENDED but not required — automatic rows carry no completeness contract.'
        ),
    ] = None
    by_keyword: Annotated[
        list[keyword_delivery_metrics.KeywordDeliveryMetrics] | 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_keyword_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether by_keyword was truncated due to the requested limit or a seller-imposed maximum. Sellers MUST return this flag whenever by_keyword is present and the request included reporting_dimensions.keyword (false means the list is complete). When the breakdown was returned automatically without a request key, the flag is RECOMMENDED but not required — automatic rows carry no completeness contract.'
        ),
    ] = None
    by_keyword_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description="The metric actually used to order by_keyword rows. Sellers MUST return this field whenever by_keyword is present and the request included reporting_dimensions.keyword. When the seller cannot sort by the requested sort_by metric it falls back to 'spend'; this echo makes the fallback visible instead of silently returning rows the buyer will misread as ordered by the requested metric. When the breakdown was returned automatically without a request key, the field is RECOMMENDED but not required — automatic rows carry no completeness contract."
        ),
    ] = None
    by_keyword_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_keyword ordering. Sellers MUST return this field whenever by_keyword is present and the request included reporting_dimensions.keyword, alongside by_keyword_sorted_by. When the breakdown was returned automatically without a request key, the field is RECOMMENDED but not required — automatic rows carry no completeness contract.'
        ),
    ] = None
    by_geo: Annotated[
        list[geo_delivery_metrics.GeoDeliveryMetrics] | 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[
        StrictBool | 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_geo_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description="The metric actually used to order by_geo rows. Sellers MUST return this field whenever by_geo is present. When the seller cannot sort by the requested sort_by metric it falls back to 'spend'; this echo makes the fallback visible instead of silently returning rows the buyer will misread as ordered by the requested metric."
        ),
    ] = None
    by_geo_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_geo ordering. Sellers MUST return this field whenever by_geo is present.'
        ),
    ] = 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[
        StrictBool | 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_device_type_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description="The metric actually used to order by_device_type rows. Sellers MUST return this field whenever by_device_type is present. When the seller cannot sort by the requested sort_by metric it falls back to 'spend'; this echo makes the fallback visible instead of silently returning rows the buyer will misread as ordered by the requested metric."
        ),
    ] = None
    by_device_type_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_device_type ordering. Sellers MUST return this field whenever by_device_type is present.'
        ),
    ] = None
    by_device_type_pagination: Annotated[
        pagination_response.PaginationResponse | None,
        Field(
            description="Cursor to retrieve the remaining by_device_type rows when by_device_type_truncated is true. Sellers MUST return this field whenever by_device_type_truncated is true; omit when false or when by_device_type is absent. Pass the cursor back in the corresponding request's reporting_dimensions.device_type_1.cursor to fetch the next page; other reporting_dimensions.device_type request fields (limit, sort_by, sort_direction) MUST be repeated unchanged across paged requests."
        ),
    ] = 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[
        StrictBool | 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_platform_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description="The metric actually used to order by_device_platform rows. Sellers MUST return this field whenever by_device_platform is present. When the seller cannot sort by the requested sort_by metric it falls back to 'spend'; this echo makes the fallback visible instead of silently returning rows the buyer will misread as ordered by the requested metric."
        ),
    ] = None
    by_device_platform_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_device_platform ordering. Sellers MUST return this field whenever by_device_platform is present.'
        ),
    ] = None
    by_device_platform_pagination: Annotated[
        pagination_response.PaginationResponse | None,
        Field(
            description="Cursor to retrieve the remaining by_device_platform rows when by_device_platform_truncated is true. Sellers MUST return this field whenever by_device_platform_truncated is true; omit when false or when by_device_platform is absent. Pass the cursor back in the corresponding request's reporting_dimensions.device_platform_1.cursor to fetch the next page; other reporting_dimensions.device_platform request fields (limit, sort_by, sort_direction) MUST be repeated unchanged across paged requests."
        ),
    ] = 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[
        StrictBool | 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_audience_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description="The metric actually used to order by_audience rows. Sellers MUST return this field whenever by_audience is present. When the seller cannot sort by the requested sort_by metric it falls back to 'spend'; this echo makes the fallback visible instead of silently returning rows the buyer will misread as ordered by the requested metric."
        ),
    ] = None
    by_audience_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_audience ordering. Sellers MUST return this field whenever by_audience is present.'
        ),
    ] = None
    by_audience_pagination: Annotated[
        pagination_response.PaginationResponse | None,
        Field(
            description="Cursor to retrieve the remaining by_audience rows when by_audience_truncated is true. Sellers MUST return this field whenever by_audience_truncated is true; omit when false or when by_audience is absent. Pass the cursor back in the corresponding request's reporting_dimensions.audience.cursor to fetch the next page; other reporting_dimensions.audience request fields (limit, sort_by, sort_direction) MUST be repeated unchanged across paged requests."
        ),
    ] = None
    by_demographic: Annotated[
        list[ByDemographicItem] | None,
        Field(
            description='Delivery by demographic within this package. Available when the buyer requests demographic breakdown and the product declares supports_demographic_breakdown. A free-form measurement code does not prove alignment with buyer targeting. When age is present it is the authoritative machine-comparable interval; for requested age_ranges, sellers MUST echo the exact requested interval and MUST NOT substitute a wider or narrower native bucket.'
        ),
    ] = None
    by_demographic_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether non-suppressed by_demographic rows were truncated due to the requested limit or a seller-imposed maximum. Sellers MUST return this flag whenever by_demographic is present. False means every non-suppressed row is present; inspect by_demographic_suppressed separately before reconciling rows to package totals.'
        ),
    ] = None
    by_demographic_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description="The metric actually used to order by_demographic rows. Sellers MUST return this field whenever by_demographic is present. When the seller cannot sort by the requested sort_by metric it falls back to 'spend'; this echo makes the fallback visible instead of silently returning rows the buyer will misread as ordered by the requested metric."
        ),
    ] = None
    by_demographic_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_demographic ordering. Sellers MUST return this field whenever by_demographic is present.'
        ),
    ] = None
    by_demographic_suppressed: Annotated[
        StrictBool | None,
        Field(
            description='Whether one or more otherwise reportable demographic rows were omitted due to privacy, policy, or measurement thresholds. Sellers MUST return this flag whenever by_demographic is present. False means no rows were threshold-suppressed.'
        ),
    ] = None
    by_placement: Annotated[
        list[placement_delivery_metrics.PlacementDeliveryMetrics] | None,
        Field(
            description='Delivery by placement within this package. placement_id remains required for 3.1 compatibility. New 3.2 sellers also emit placement_identity, whose discriminator separates publisher-catalog identity from sales-agent-defined inline identity.'
        ),
    ] = None
    by_placement_truncated: Annotated[
        StrictBool | 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
    by_placement_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description="The metric actually used to order by_placement rows. Sellers MUST return this field whenever by_placement is present. When the seller cannot sort by the requested sort_by metric it falls back to 'spend'; this echo makes the fallback visible instead of silently returning rows the buyer will misread as ordered by the requested metric."
        ),
    ] = None
    by_placement_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_placement ordering. Sellers MUST return this field whenever by_placement is present.'
        ),
    ] = None
    by_placement_pagination: Annotated[
        pagination_response.PaginationResponse | None,
        Field(
            description="Cursor to retrieve the remaining by_placement rows when by_placement_truncated is true. Sellers MUST return this field whenever by_placement_truncated is true; omit when false or when by_placement is absent. Pass the cursor back in the corresponding request's reporting_dimensions.placement.cursor to fetch the next page; other reporting_dimensions.placement request fields (limit, sort_by, sort_direction) MUST be repeated unchanged across paged requests."
        ),
    ] = None
    by_property: Annotated[
        list[property_delivery_metrics.PropertyDeliveryMetrics] | None,
        Field(
            description='Delivery by publisher property within this package. Each row identifies the actual surface with an operational identifier and adds property_ref when it resolves to a canonical publisher catalog entry. Rows are independent of by_collection.'
        ),
    ] = None
    by_property_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether non-suppressed by_property rows were truncated. Sellers MUST return this flag whenever by_property is present. False means every non-suppressed row is present; inspect by_property_suppressed before reconciling rows to package totals.'
        ),
    ] = None
    by_property_suppressed: Annotated[
        StrictBool | None,
        Field(
            description='Whether one or more otherwise reportable by_property rows were omitted from this response due to privacy, policy, or measurement thresholds. Sellers MUST return this flag whenever by_property is present. False means no rows were threshold-suppressed. Both suppression and truncation may be true.'
        ),
    ] = None
    by_property_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description='The metric actually used to order by_property rows. Sellers MUST return this field whenever by_property is present.'
        ),
    ] = None
    by_property_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_property rows. Sellers MUST return this field whenever by_property is present.'
        ),
    ] = None
    by_collection: Annotated[
        list[collection_delivery_metrics.CollectionDeliveryMetrics] | None,
        Field(
            description='Delivery by publisher-scoped collection within this package. This is a marginal breakdown and does not by itself prove which property carried a collection.'
        ),
    ] = None
    by_collection_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether by_collection was truncated. Sellers MUST return this flag whenever by_collection is present.'
        ),
    ] = None
    by_collection_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description='The metric actually used to order by_collection rows. Sellers MUST return this field whenever by_collection is present.'
        ),
    ] = None
    by_collection_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_collection rows. Sellers MUST return this field whenever by_collection is present.'
        ),
    ] = None
    by_installment: Annotated[
        list[installment_delivery_metrics.InstallmentDeliveryMetrics] | None,
        Field(description='Delivery by canonically identified installment within this package.'),
    ] = None
    by_installment_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether by_installment was truncated. Sellers MUST return this flag whenever by_installment is present.'
        ),
    ] = None
    by_installment_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description='The metric actually used to order by_installment rows. Sellers MUST return this field whenever by_installment is present.'
        ),
    ] = None
    by_installment_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_installment rows. Sellers MUST return this field whenever by_installment is present.'
        ),
    ] = None
    by_collection_property: Annotated[
        list[collection_property_delivery_metrics.CollectionPropertyDeliveryMetrics] | None,
        Field(
            description='Delivery at the collection × property intersection. A row is affirmative delivery evidence that the referenced collection ran on the referenced property; it is not merely a carriage or catalog assertion.'
        ),
    ] = None
    by_collection_property_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether non-suppressed by_collection_property rows were truncated. Sellers MUST return this flag whenever by_collection_property is present.'
        ),
    ] = None
    by_collection_property_suppressed: Annotated[
        StrictBool | None,
        Field(
            description='Whether one or more otherwise reportable by_collection_property rows were omitted from this response due to privacy, policy, or measurement thresholds. Sellers MUST return this flag whenever by_collection_property is present. False means no rows were threshold-suppressed. Both suppression and truncation may be true.'
        ),
    ] = None
    by_collection_property_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description='The metric actually used to order by_collection_property rows. Sellers MUST return this field whenever by_collection_property is present.'
        ),
    ] = None
    by_collection_property_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_collection_property rows. Sellers MUST return this field whenever by_collection_property is present.'
        ),
    ] = None
    by_installment_property: Annotated[
        list[installment_property_delivery_metrics.InstallmentPropertyDeliveryMetrics] | None,
        Field(
            description='Delivery at the installment × property intersection. A row is affirmative delivery evidence that the referenced airing, episode, issue, or programming block ran on the referenced property; it is not inferred from collection carriage or independent marginals.'
        ),
    ] = None
    by_installment_property_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether non-suppressed by_installment_property rows were truncated. Sellers MUST return this flag whenever by_installment_property is present.'
        ),
    ] = None
    by_installment_property_suppressed: Annotated[
        StrictBool | None,
        Field(
            description='Whether one or more otherwise reportable by_installment_property rows were omitted from this response due to privacy, policy, or measurement thresholds. Sellers MUST return this flag whenever by_installment_property is present. False means no rows were threshold-suppressed. Both suppression and truncation may be true.'
        ),
    ] = None
    by_installment_property_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description='The metric actually used to order by_installment_property rows. Sellers MUST return this field whenever by_installment_property is present.'
        ),
    ] = None
    by_installment_property_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_installment_property rows. Sellers MUST return this field whenever by_installment_property is present.'
        ),
    ] = None
    by_placement_property: Annotated[
        list[placement_property_delivery_metrics.PlacementPropertyDeliveryMetrics] | None,
        Field(
            description='Delivery at the placement × property intersection. This proves which actual property carried a placement that may span more than one property.'
        ),
    ] = None
    by_placement_property_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether non-suppressed by_placement_property rows were truncated. Sellers MUST return this flag whenever by_placement_property is present.'
        ),
    ] = None
    by_placement_property_suppressed: Annotated[
        StrictBool | None,
        Field(
            description='Whether one or more otherwise reportable by_placement_property rows were omitted from this response due to privacy, policy, or measurement thresholds. Sellers MUST return this flag whenever by_placement_property is present. False means no rows were threshold-suppressed. Both suppression and truncation may be true.'
        ),
    ] = None
    by_placement_property_sorted_by: Annotated[
        sort_metric.SortMetric | None,
        Field(
            description='The metric actually used to order by_placement_property rows. Sellers MUST return this field whenever by_placement_property is present.'
        ),
    ] = None
    by_placement_property_sort_direction: Annotated[
        sort_direction.SortDirection | None,
        Field(
            description='The direction actually applied to by_placement_property rows. Sellers MUST return this field whenever by_placement_property is present.'
        ),
    ] = None
    by_spot: Annotated[
        list[BySpotItem] | None,
        Field(
            description='Spot-level as-run airing records for broadcast TV, radio, or other scheduled inventory. Available when the buyer requests spot breakdown and the product declares supports_spot_breakdown. Sellers MUST order rows by aired_at ascending. The same spot_id is reused when a later package measurement_window adds or revises metrics. Network and station are optional so station-direct radio and network-level TV records use the same channel-neutral shape. Sellers SHOULD populate creative_id whenever they can associate a specific airing with a creative, particularly when the package could serve more than one creative during any part of the reporting period, including a mid-period replacement. Sellers that cannot make that association MUST omit creative_id rather than emit a default or placeholder. Buyers MUST NOT aggregate by_spot rows by creative_id and expect the result to equal by_creative impressions for the same creative: the two dimensions are independently produced at different granularities and are subject to different metric-maturation and attribution semantics within the package measurement_window. by_creative is the authoritative creative-performance aggregate; creative_id on a by_spot row identifies which creative aired, not an independent metric roll-up source.'
        ),
    ] = None
    by_spot_truncated: Annotated[
        StrictBool | None,
        Field(
            description='Whether by_spot is incomplete because of the requested limit or a seller-imposed maximum. Sellers MUST return this flag whenever by_spot is present (false means the as-run log is complete for the requested reporting period).'
        ),
    ] = None
    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
    spend: Any

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var by_audience : list[ByAudienceItem] | None
var by_audience_pagination : PaginationResponse | None
var by_audience_sort_direction : SortDirection | None
var by_audience_sorted_by : SortMetric | None
var by_audience_truncated : bool | None
var by_catalog_item : list[CatalogItemDeliveryMetrics] | None
var by_catalog_item_sort_direction : SortDirection | None
var by_catalog_item_sorted_by : SortMetric | None
var by_catalog_item_truncated : bool | None
var by_collection : list[CollectionDeliveryMetrics] | None
var by_collection_property : list[CollectionPropertyDeliveryMetrics] | None
var by_collection_property_sort_direction : SortDirection | None
var by_collection_property_sorted_by : SortMetric | None
var by_collection_property_suppressed : bool | None
var by_collection_property_truncated : bool | None
var by_collection_sort_direction : SortDirection | None
var by_collection_sorted_by : SortMetric | None
var by_collection_truncated : bool | None
var by_creative : list[CreativeDeliveryMetrics] | None
var by_creative_sort_direction : SortDirection | None
var by_creative_sorted_by : SortMetric | None
var by_creative_truncated : bool | None
var by_demographic : list[ByDemographicItem] | None
var by_demographic_sort_direction : SortDirection | None
var by_demographic_sorted_by : SortMetric | None
var by_demographic_suppressed : bool | None
var by_demographic_truncated : bool | None
var by_device_platform : list[ByDevicePlatformItem] | None
var by_device_platform_pagination : PaginationResponse | None
var by_device_platform_sort_direction : SortDirection | None
var by_device_platform_sorted_by : SortMetric | None
var by_device_platform_truncated : bool | None
var by_device_type : list[ByDeviceTypeItem] | None
var by_device_type_pagination : PaginationResponse | None
var by_device_type_sort_direction : SortDirection | None
var by_device_type_sorted_by : SortMetric | None
var by_device_type_truncated : bool | None
var by_format : list[ByFormatItem] | None
var by_format_sort_direction : SortDirection | None
var by_format_sorted_by : SortMetric | None
var by_format_truncated : bool | None
var by_geo : list[GeoDeliveryMetrics] | None
var by_geo_sort_direction : SortDirection | None
var by_geo_sorted_by : SortMetric | None
var by_geo_truncated : bool | None
var by_installment : list[InstallmentDeliveryMetrics] | None
var by_installment_property : list[InstallmentPropertyDeliveryMetrics] | None
var by_installment_property_sort_direction : SortDirection | None
var by_installment_property_sorted_by : SortMetric | None
var by_installment_property_suppressed : bool | None
var by_installment_property_truncated : bool | None
var by_installment_sort_direction : SortDirection | None
var by_installment_sorted_by : SortMetric | None
var by_installment_truncated : bool | None
var by_keyword : list[KeywordDeliveryMetrics] | None
var by_keyword_sort_direction : SortDirection | None
var by_keyword_sorted_by : SortMetric | None
var by_keyword_truncated : bool | None
var by_placement : list[PlacementDeliveryMetrics] | None
var by_placement_pagination : PaginationResponse | None
var by_placement_property : list[PlacementPropertyDeliveryMetrics] | None
var by_placement_property_sort_direction : SortDirection | None
var by_placement_property_sorted_by : SortMetric | None
var by_placement_property_suppressed : bool | None
var by_placement_property_truncated : bool | None
var by_placement_sort_direction : SortDirection | None
var by_placement_sorted_by : SortMetric | None
var by_placement_truncated : bool | None
var by_property : list[PropertyDeliveryMetrics] | None
var by_property_sort_direction : SortDirection | None
var by_property_sorted_by : SortMetric | None
var by_property_suppressed : bool | None
var by_property_truncated : bool | None
var by_spot : list[BySpotItem] | None
var by_spot_truncated : bool | None
var currency : str
var daily_breakdown : list[DailyBreakdownItem] | None
var delivery_status : DeliveryStatus | None
var finalized_at : pydantic.types.AwareDatetime | None
var is_final : bool | None
var measurement_window : str | None
var metric_values : list[PackageDeliveryMetricValue] | None
var missing_metrics : list[MissingMetric1 | MissingMetric2] | None
var model_config
var pacing_index : float | None
var package_id : str
var paused : bool | None
var pricing_model : PricingModel
var rate : float
var spend : Any
var supersedes_window : str | None

Inherited members

class CanonicalMediaBuyActionMode (*args, **kwds)
Expand source code
class CanonicalMediaBuyActionMode(StrEnum):
    self_serve = 'self_serve'
    conditional_self_serve = 'conditional_self_serve'
    seller_managed = 'seller_managed'
    requires_approval = 'requires_approval'

Enum where members are also (and must be) strings

Ancestors

  • enum.StrEnum
  • builtins.str
  • enum.ReprEnum
  • enum.Enum

Class variables

var conditional_self_serve
var requires_approval
var self_serve
var seller_managed
class CanonicalMediaBuyActionName (*args, **kwds)
Expand source code
class CanonicalMediaBuyActionName(StrEnum):
    pause = 'pause'
    resume = 'resume'
    cancel = 'cancel'
    extend_flight = 'extend_flight'
    shorten_flight = 'shorten_flight'
    update_flight_dates = 'update_flight_dates'
    increase_budget = 'increase_budget'
    decrease_budget = 'decrease_budget'
    reallocate_budget = 'reallocate_budget'
    update_budget_allocation = 'update_budget_allocation'
    update_targeting = 'update_targeting'
    update_pacing = 'update_pacing'
    update_bidding = 'update_bidding'
    update_frequency_caps = 'update_frequency_caps'
    update_media_buy_frequency_cap = 'update_media_buy_frequency_cap'
    update_catalog_assignments = 'update_catalog_assignments'
    update_keywords = 'update_keywords'
    update_optimization_goals = 'update_optimization_goals'
    update_impression_goal = 'update_impression_goal'
    update_spend_target = 'update_spend_target'
    update_reporting_webhook = 'update_reporting_webhook'
    replace_creative = 'replace_creative'
    update_creative_assignments = 'update_creative_assignments'
    remove_creative = 'remove_creative'
    add_packages = 'add_packages'
    remove_packages = 'remove_packages'

Enum where members are also (and must be) strings

Ancestors

  • enum.StrEnum
  • builtins.str
  • enum.ReprEnum
  • enum.Enum

Class variables

var add_packages
var cancel
var decrease_budget
var extend_flight
var increase_budget
var pause
var reallocate_budget
var remove_creative
var remove_packages
var replace_creative
var resume
var shorten_flight
var update_bidding
var update_budget_allocation
var update_catalog_assignments
var update_creative_assignments
var update_flight_dates
var update_frequency_caps
var update_impression_goal
var update_keywords
var update_media_buy_frequency_cap
var update_optimization_goals
var update_pacing
var update_reporting_webhook
var update_spend_target
var update_targeting
class CanonicalProductAction (**data: Any)
Expand source code
class CanonicalProductAction(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    action: canonical_media_buy_action.CanonicalMediaBuyActionName
    modes: Annotated[
        list[canonical_media_buy_action_mode.CanonicalMediaBuyActionMode], Field(min_length=1)
    ]
    allowed_statuses: Annotated[
        list[media_buy_status.MediaBuyStatus] | None, Field(min_length=1)
    ] = None
    sla: sla_window.SlaWindow | None = None
    constraints: Annotated[
        change_term_constraints.MediaBuyChangeTermConstraints | None,
        Field(
            description='Advisory machine-readable bounds for product selection; proposal change terms restate binding bounds.'
        ),
    ] = None
    terms_ref: Annotated[
        str | None,
        Field(
            description='Optional advisory pointer to published commercial terms. It is not a proposal change-term identity.'
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var action : CanonicalMediaBuyActionName
var allowed_statuses : list[MediaBuyStatus] | None
var constraints : MediaBuyChangeTermConstraints1 | MediaBuyChangeTermConstraints2 | MediaBuyChangeTermConstraints3 | MediaBuyChangeTermConstraints4 | None
var model_config
var modes : list[CanonicalMediaBuyActionMode]
var sla : SlaWindow | None
var terms_ref : str | None

Inherited members

class CommercialTerms (**data: Any)
Expand source code
class CommercialTerms(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    source_feed_version: Annotated[
        str | None,
        Field(
            description='Wholesale product feed version against which direct published offers were accepted. Omitted when the seller authored terms outside a wholesale snapshot.',
            min_length=1,
        ),
    ] = None
    source_pricing_version: Annotated[
        str | None,
        Field(
            description='Pricing-layer version against which published rates were accepted.',
            min_length=1,
        ),
    ] = None
    brand: brand_key.BrandKey
    advertiser_industry: advertiser_industry_1.AdvertiserIndustry | None = None
    purchases: Annotated[
        list[product_purchase.ProductPurchase],
        Field(
            description='Exact canonical product, pricing, format, catalog, budget, targeting, bidding, optimization, resolved flight, measurement, and performance terms in the commercial envelope.',
            min_length=1,
        ),
    ]
    start_time: start_timing.StartTiming
    end_time: AwareDatetime
    total_budget: TotalBudget | None = None
    daily_budget_cap: Annotated[
        StrictFloat | None,
        Field(
            description='Hard aggregate daily spend ceiling accepted as part of these terms. It bounds total spend without creating purchase allocations.',
            ge=0.0,
        ),
    ] = None
    frequency_cap: Annotated[
        media_buy_frequency_cap.MediaBuyFrequencyCap | None,
        Field(
            description='Hard MediaBuy-level cap accepted as part of these terms. One counter aggregates exposures across every purchase; purchase targeting caps remain independently binding.'
        ),
    ] = None
    budget_cap_timezone: Annotated[
        str | None,
        Field(
            description='Shared IANA calendar-day boundary for aggregate and purchase daily caps in these terms.',
            min_length=1,
        ),
    ] = None
    budget_allocation: canonical_budget_allocation.CanonicalBudgetAllocation | None = None
    pacing: pacing_1.Pacing | None = None
    bidding: Annotated[
        bidding_policy.BiddingPolicy | None,
        Field(
            description="Media-buy bidding policy. A proposal answering criteria.outcome_target.cost_per states here the cost the seller can plan to, which the buyer adopts on acceptance: the requested strength, and an amount greater than or equal to the ask (the ask when the seller can forecast goal volume under it within the buyer's budget, otherwise the lowest such amount), denominated in the purchases' pricing currency, which equals cost_per.currency. It is an execution control, not an expected price; when the planned spend at that amount is below total_budget, forecast points carry metrics.spend. See outcome-target.json for goal binding."
        ),
    ] = None
    invoice_recipient: business_entity.BusinessEntity | None = None
    purchase_order_ref: Annotated[str | None, Field(max_length=255, min_length=1)] = None
    agency_estimate_number: Annotated[str | None, Field(max_length=100)] = None
    reporting_commitments: Annotated[
        list[ReportingCommitment] | None,
        Field(
            description='Binding reporting contract keyed by position in purchases. Amendments preserve prior entries and add metrics with effective_at; seller-assigned package IDs live in the execution binding, outside this digest.',
            min_length=1,
        ),
    ] = None
    cancellation_terms: CancellationTerms | None = None
    change_terms: Annotated[
        list[change_term.MediaBuyChangeTerm] | None,
        Field(
            description='Binding buyer change rights included in the commercial envelope and therefore covered by terms_digest. Entries are uniquely keyed by action. When this field is present, an omitted action is not a negotiated change right. Omission of the entire field means legacy-unspecified rights, not a prohibition.',
            min_length=1,
        ),
    ] = None

    @model_validator(mode='after')
    def _validate_change_term_set(self) -> CommercialTerms:
        if self.change_terms is None:
            return self
        actions = [term.action.value for term in self.change_terms]
        term_ids = [term.term_id for term in self.change_terms]
        if len(set(actions)) != len(actions):
            raise ValueError('change_terms must be uniquely keyed by action')
        if len(set(term_ids)) != len(term_ids):
            raise ValueError('change_terms term_id values must be unique')
        currencies = set()
        for purchase in self.purchases:
            if purchase.pricing is None:
                raise ValueError('accepted commercial-term purchases require resolved pricing')
            currencies.add(purchase.pricing.currency)
        for term in self.change_terms:
            if term.constraints is None:
                continue
            constraint = term.constraints
            if constraint.kind == 'budget':
                money_fields = (
                    constraint.max_delta_amount,
                    constraint.min_result_amount,
                    constraint.max_result_amount,
                )
                if any(money is not None and money.currency not in currencies for money in money_fields):
                    raise ValueError('change-term monetary constraint currency must match purchases')
                if (
                    constraint.min_result_amount is not None
                    and constraint.max_result_amount is not None
                    and constraint.min_result_amount.amount > constraint.max_result_amount.amount
                ):
                    raise ValueError('change-term minimum result exceeds maximum result')
            elif constraint.kind == 'flight':
                if (
                    constraint.earliest_result is not None
                    and constraint.latest_result is not None
                    and constraint.earliest_result > constraint.latest_result
                ):
                    raise ValueError('change-term earliest result exceeds latest result')
            elif constraint.kind == 'effective_timing' and (
                constraint.earliest_effective_at is not None
                and constraint.latest_effective_at is not None
                and constraint.earliest_effective_at > constraint.latest_effective_at
            ):
                raise ValueError('change-term earliest effective time exceeds latest time')
        return self

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var advertiser_industry : AdvertiserIndustry | None
var agency_estimate_number : str | None
var bidding : BiddingPolicy | None
var brand : BrandKey
var budget_allocation : CanonicalBudgetAllocation1 | CanonicalBudgetAllocation2 | None
var budget_cap_timezone : str | None
var cancellation_terms : CancellationTerms | None
var change_terms : list[MediaBuyChangeTerm] | None
var daily_budget_cap : float | None
var end_time : pydantic.types.AwareDatetime
var frequency_cap : MediaBuyFrequencyCap | None
var invoice_recipient : BusinessEntity | None
var model_config
var pacing : Pacing | None
var purchase_order_ref : str | None
var purchases : list[ProductPurchase]
var reporting_commitments : list[ReportingCommitment] | None
var source_feed_version : str | None
var source_pricing_version : str | None
var start_time : Literal['asap'] | pydantic.types.AwareDatetime
var total_budget : TotalBudget | None

Inherited members

class CompatibilityPurchaseCoordinatorInput (**data: Any)
Expand source code
class CompatibilityPurchaseCoordinatorInput(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    idempotency_key: Annotated[
        UUID,
        Field(
            description='Replay identity for this logical coordinator operation. Exact retries resume the durable operation record instead of redeeming the continuation again.'
        ),
    ]
    continuation_token: Annotated[
        str,
        Field(
            description='Opaque token returned by products_available.purchase_continuation.',
            min_length=16,
        ),
    ]
    account: Annotated[
        account_ref.AccountReference,
        Field(
            description='Account identity that must match the account bound into the continuation token.'
        ),
    ]
    selected_product_ids: Annotated[
        list[SelectedProductId],
        Field(
            description='Non-empty subset of the product IDs bound into the continuation.',
            min_length=1,
            json_schema_extra={'uniqueItems': True},
        ),
    ]
    accepted_losses: Annotated[
        list[AcceptedLoss],
        Field(
            description='Exact loss set returned with the continuation. Missing, extra, or stale consent fails before mutation.',
            min_length=2,
            json_schema_extra={
                'uniqueItems': True,
                'allOf': [
                    {'contains': {'const': 'feed_version_not_atomic'}},
                    {'contains': {'const': 'pricing_version_not_atomic'}},
                ],
            },
        ),
    ]
    legacy_create_request: Annotated[
        dict[str, Any],
        Field(
            description='Proposed create_media_buy payload. Before mutation the coordinator validates this object against create-media-buy-request.json from source_adcp_version, requires explicit-package mode, and requires its package product IDs to equal selected_product_ids.',
            min_length=1,
        ),
    ]


    @field_validator('selected_product_ids')
    @classmethod
    def _selected_product_ids_are_unique(
        cls, values: list[SelectedProductId]
    ) -> list[SelectedProductId]:
        if len(values) != len(set(values)):
            raise ValueError('selected_product_ids must contain unique items')
        return values

    @field_validator('accepted_losses')
    @classmethod
    def _accepted_losses_match_schema(
        cls, values: list[AcceptedLoss]
    ) -> list[AcceptedLoss]:
        value_set = set(values)
        if len(values) != len(value_set):
            raise ValueError('accepted_losses must contain unique items')
        required = {
            AcceptedLoss.feed_version_not_atomic,
            AcceptedLoss.pricing_version_not_atomic,
        }
        if not required.issubset(value_set):
            raise ValueError('accepted_losses must include the required compatibility losses')
        return values

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var accepted_losses : list[AcceptedLoss]
var account : AccountReference1 | AccountReference2
var continuation_token : str
var idempotency_key : uuid.UUID
var legacy_create_request : dict[str, typing.Any]
var model_config
var selected_product_ids : list[SelectedProductId]

Inherited members

class ControlMediaBuyRequest (**data: Any)
Expand source code
class ControlMediaBuyRequest(AdcpRequest, AdcpVersionEnvelope):
    model_config = ConfigDict(
        extra='forbid',
    )
    idempotency_key: Annotated[
        str, Field(max_length=255, min_length=16, pattern='^[A-Za-z0-9_.:-]{16,255}$')
    ]
    account: canonical_account_ref.CanonicalAccountReference
    media_buy_id: Annotated[str, Field(min_length=1)]
    revision: Annotated[
        SchemaInt,
        Field(
            description='Required optimistic-concurrency revision from the latest MediaBuy snapshot.',
            ge=1,
        ),
    ]
    name: Annotated[
        str | None,
        Field(
            description='Replace the human-readable MediaBuy name as revision-checked operational metadata. This display label is not an identifier, financial reference, or change to the accepted commercial terms.',
            max_length=255,
            min_length=1,
            pattern='\\S',
        ),
    ] = None
    paused: StrictBool | None = None
    canceled: Annotated[
        Literal[True] | None,
        Field(
            description='Exercise an already-accepted unilateral cancellation right. A cancellation requiring seller agreement is requested by refining the accepted proposal.'
        ),
    ] = None
    cancellation_reason: Annotated[str | None, Field(max_length=500, min_length=1)] = None
    total_budget: TotalBudget | None = None
    daily_budget_cap: Annotated[
        StrictFloat | None,
        Field(
            description='Replace the hard aggregate daily cap; null removes it. Numeric changes apply immediately with current-cap-day spend counted and do not redistribute purchase caps.',
            ge=0.0,
        ),
    ] = None
    frequency_cap: Annotated[
        media_buy_frequency_cap.MediaBuyFrequencyCap | None,
        Field(
            description='Replace the shared MediaBuy frequency cap; null removes it. The change applies immediately without resetting counters: qualifying prior exposures still count in the resulting active window. Sellers MUST reject the complete mutation with UNSUPPORTED_FEATURE, before any change, if the cap is outside declared constraints or any active package cannot participate in the resulting shared counter, and MUST NOT clamp it. Requires update_media_buy_frequency_cap in available_actions.'
        ),
    ] = None
    budget_cap_timezone: Annotated[
        str | None,
        Field(
            description='Replace the shared IANA cap-day timezone override; null restores the default selected by budget_capping.timezone_basis (Account.timezone or fixed_timezone). A timezone change begins at the next boundary under the previously effective timezone.',
            min_length=1,
        ),
    ] = None
    budget_allocation: canonical_budget_allocation.CanonicalBudgetAllocation | None = None
    pacing: Annotated[
        pacing_1.Pacing | None,
        Field(
            description='Replace aggregate media-buy pacing. In seller-optimized allocation, a seller declaring media_buy.features.seller_optimized_budget MUST accept omission and `even`; it MAY reject `asap` or `front_loaded` with UNSUPPORTED_FEATURE (error.field `pacing`) before any provider mutation and MUST NOT silently coerce them to `even`. Fixed-allocation semantics are unchanged.'
        ),
    ] = None
    bidding: bidding_policy.BiddingPolicy | None = None
    packages: Annotated[
        list[package_control.PackageControl] | None,
        Field(
            description='Operational patches keyed by package_id. Each package_id MUST appear at most once; sellers reject duplicate IDs atomically.',
            min_length=1,
        ),
    ] = None
    reporting_webhook: reporting_webhook_1.ReportingWebhook | None = None
    governance_context: Annotated[str | None, Field(max_length=4096, min_length=1)] = None
    push_notification_config: push_notification_config_1.PushNotificationConfig | None = None
    context: context_1.ContextObject | None = None
    ext: ext_1.ExtensionObject | None = None

    @model_validator(mode='after')
    def _require_schema_required_group(self) -> ControlMediaBuyRequest:
        # ``required`` asks whether the caller supplied the field, which is what
        # model_fields_set answers. An explicit null is a supplied value — on a
        # mutation input it is the command to clear — and a default the caller
        # never sent is not.
        for group in (('name',), ('paused',), ('canceled',), ('total_budget',), ('daily_budget_cap',), ('frequency_cap',), ('budget_cap_timezone',), ('budget_allocation',), ('pacing',), ('bidding',), ('packages',), ('reporting_webhook',),):
            if all(name in self.model_fields_set for name in group):
                return self
        raise ValueError(
            'ControlMediaBuyRequest requires at least one of these field groups: name | paused | canceled | total_budget | daily_budget_cap | frequency_cap | budget_cap_timezone | budget_allocation | pacing | bidding | packages | reporting_webhook'
        )

The request message of a task in the pinned bundle's task registry.

A consumer holding one can resolve its account, decide at-most-once, echo its context and negotiate version – the whole transport-boundary job – before knowing which tool it is. issubclass(model, AdcpRequest) is the registration-time proof that a model is spec-derived rather than a hand-written parallel: a field test passes for a forged model, descent does not.

Each accessor returns the field's value, or None when this tool's schema declares no such field. Only 49 of the 87 request schemas declare an account and only 43 an idempotency_key, so asking the request is what replaces getattr(req, "account", None) against Any at the boundary.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var account : CanonicalAccountReference1 | CanonicalAccountReference2
var bidding : BiddingPolicy | None
var budget_allocation : CanonicalBudgetAllocation1 | CanonicalBudgetAllocation2 | None
var budget_cap_timezone : str | None
var canceled : Literal[True] | None
var cancellation_reason : str | None
var context : ContextObject | None
var daily_budget_cap : float | None
var ext : ExtensionObject | None
var frequency_cap : MediaBuyFrequencyCap | None
var governance_context : str | None
var idempotency_key : str
var media_buy_id : str
var model_config
var name : str | None
var pacing : Pacing | None
var packages : list[PackageControl] | None
var paused : bool | None
var push_notification_config : PushNotificationConfig | None
var reporting_webhook : ReportingWebhook | None
var revision : int
var total_budget : TotalBudget | None

Instance variables

var adcp_major_version : int | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.

Inherited members

class CreateMediaBuyRequest (**data: Any)
Expand source code
class CreateMediaBuyRequest(_LegacyCreateMediaBuyRequest, CanonicalBoundaryModel):
    """Canonical create request; packages are canonical package requests."""

    packages: list[PackageRequest] | None = None

Canonical create request; packages are canonical package requests.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var packages : list[PackageRequest] | None

Inherited members

class CreateMediaBuySuccessResponse (**data: Any)
Expand source code
class CreateMediaBuyResponse1(_LegacyCreateMediaBuyResponse1, CanonicalBoundaryModel):
    """Canonical create response preserving the 3.x legacy-status normalizer."""

    packages: list[Package]  # type: ignore[assignment]

    @model_validator(mode="before")
    @classmethod
    def _normalize_legacy_status(cls, data: Any) -> Any:
        if not isinstance(data, dict):
            return data
        raw_status = unwrap_enum_value(data.get("status"))
        media_buy_status = unwrap_enum_value(data.get("media_buy_status"))
        if raw_status is None or raw_status == "completed":
            return {**data, "status": "completed"}
        if media_buy_status is None and raw_status in MEDIA_BUY_LEGACY_STATUS_VALUES:
            return {**data, "media_buy_status": raw_status, "status": "completed"}
        if media_buy_status is not None and raw_status == media_buy_status:
            return {**data, "status": "completed"}
        return data

Canonical create response preserving the 3.x legacy-status normalizer.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var packages : list[Package]

Inherited members

class CreateMediaBuyErrorResponse (**data: Any)
Expand source code
class CreateMediaBuyResponse2(_LegacyCreateMediaBuyResponse2, CanonicalBoundaryModel):
    """Canonical create-media-buy error arm."""

Canonical create-media-buy error arm.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members

class CreateMediaBuySubmittedResponse (**data: Any)
Expand source code
class CreateMediaBuyResponse3(_LegacyCreateMediaBuyResponse3, CanonicalBoundaryModel):
    """Canonical create-media-buy submitted arm."""

Canonical create-media-buy submitted arm.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members

class DailyBreakdownItem (**data: Any)
Expand source code
class DailyBreakdownItem(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    date: Annotated[
        str,
        Field(
            description="Calendar date (YYYY-MM-DD) in the reporting timezone: reporting_period.timezone when present, otherwise this package's product reporting_capabilities.timezone. The row covers that local day, which can be 23 or 25 hours long across a DST change.",
            pattern='^\\d{4}-\\d{2}-\\d{2}$',
        ),
    ]
    impressions: Annotated[
        StrictFloat, Field(description='Daily impressions for this package', ge=0.0)
    ]
    spend: Annotated[StrictFloat, Field(description='Daily spend for this package', ge=0.0)]
    conversions: Annotated[
        StrictFloat | None, Field(description='Daily conversions for this package', ge=0.0)
    ] = None
    conversion_value: Annotated[
        StrictFloat | None, Field(description='Daily conversion value for this package', ge=0.0)
    ] = None
    commissionable_value: Annotated[
        StrictFloat | None,
        Field(
            description='Daily settled conversion value eligible for revenue-share commission for this package',
            ge=0.0,
        ),
    ] = None
    roas: Annotated[
        StrictFloat | None,
        Field(description='Daily return on ad spend (conversion_value / spend)', ge=0.0),
    ] = None
    new_to_brand_rate: Annotated[
        StrictFloat | None,
        Field(
            description='Daily fraction of conversions from first-time brand buyers (0 = none, 1 = all)',
            ge=0.0,
            le=1.0,
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var commissionable_value : float | None
var conversion_value : float | None
var conversions : float | None
var date : str
var impressions : float
var model_config
var new_to_brand_rate : float | None
var roas : float | None
var spend : float

Inherited members

class DaypartTarget (**data: Any)
Expand source code
class DaypartTarget(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    days: Annotated[
        list[day_of_week.DayOfWeek],
        Field(
            description='Days of week this window applies to. Use multiple days for compact targeting (e.g., monday-friday in one object).',
            min_length=1,
        ),
    ]
    start_hour: Annotated[
        SchemaInt,
        Field(
            description='Start hour (inclusive), 0-23 in 24-hour format. 0 = midnight, 6 = 6:00am, 18 = 6:00pm.',
            ge=0,
            le=23,
        ),
    ]
    end_hour: Annotated[
        SchemaInt,
        Field(
            description='End hour (exclusive), 1-24 in 24-hour format. 10 = 10:00am, 24 = midnight. Must be greater than start_hour.',
            ge=1,
            le=24,
        ),
    ]
    timezone: Annotated[
        Literal['inventory_local'] | iana_timezone.IanaTimezoneIdentifier | None,
        Field(
            description="Civil-time clock used to evaluate this window. 'inventory_local' evaluates the hours in the seller-assigned local timezone of each inventory unit that can deliver the impression, such as a screen, venue, station, or publisher property; it never means the buyer, account, or server timezone. A concrete IANA timezone identifier (for example, 'America/New_York', 'CET', or 'UTC') evaluates one shared civil-time clock across the targeted inventory. Omission defaults to 'inventory_local'. Buyers that begin with a user or account preference MUST resolve it to a concrete IANA identifier before sending the daypart; 'user_timezone' and 'account_timezone' are not wire values. For each candidate delivery instant, convert the instant into this clock and compare its resulting local day and hour with the half-open window: a skipped DST hour has no matching instants, while both occurrences of a repeated hour match. This delivery clock is independent of reporting_capabilities.timezone.",
            validate_default=True,
        ),
    ] = 'inventory_local'
    label: Annotated[
        str | None,
        Field(
            description="Optional human-readable name for this time window (e.g., 'Morning Drive', 'Prime Time')"
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var days : list[DayOfWeek]
var end_hour : int
var label : str | None
var model_config
var start_hour : int
var timezone : Literal['inventory_local'] | IanaTimezoneIdentifier | None

Inherited members

class DeclineProposalsRequest (**data: Any)
Expand source code
class DeclineProposalsRequest(AdcpRequest, AdcpVersionEnvelope):
    model_config = ConfigDict(
        extra='forbid',
    )
    context_id: Annotated[
        str | None,
        Field(
            description='MCP compatibility field: servers ignore this value; A2A uses transport-native Message/Task contextId.',
            min_length=1,
        ),
    ] = None
    context: context_1.ContextObject | None = None
    governance_context: Annotated[str | None, Field(max_length=4096, min_length=1)] = None
    push_notification_config: push_notification_config_1.PushNotificationConfig | None = None
    idempotency_key: Annotated[
        str,
        Field(
            description='Client-generated key required for retry-safe proposal decline.',
            max_length=255,
            min_length=16,
            pattern='^[A-Za-z0-9_.:-]{16,255}$',
        ),
    ]
    declines: Annotated[
        list[proposal_decline.ProposalDecline],
        Field(
            description='Proposal declines to apply. proposal_id is the semantic uniqueness key and values MUST be unique even when two entries otherwise differ; implementations enforce this rule because JSON Schema uniqueItems only compares whole objects. Results preserve request order.',
            max_length=25,
            min_length=1,
        ),
    ]
    opportunity: Annotated[
        opportunity_context.OpportunityContext | None,
        Field(
            description='Optional planning-cycle update. Every named proposal MUST belong to this opportunity_id. Sellers apply the update only when every result is declined; if any result is unable, the opportunity remains unchanged. Use status closed when these declines end the broader opportunity.'
        ),
    ] = None

The request message of a task in the pinned bundle's task registry.

A consumer holding one can resolve its account, decide at-most-once, echo its context and negotiate version – the whole transport-boundary job – before knowing which tool it is. issubclass(model, AdcpRequest) is the registration-time proof that a model is spec-derived rather than a hand-written parallel: a field test passes for a forged model, descent does not.

Each accessor returns the field's value, or None when this tool's schema declares no such field. Only 49 of the 87 request schemas declare an account and only 43 an idempotency_key, so asking the request is what replaces getattr(req, "account", None) against Any at the boundary.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var context : ContextObject | None
var context_id : str | None
var declines : list[ProposalDecline]
var governance_context : str | None
var idempotency_key : str
var model_config
var opportunity : OpportunityContext | None
var push_notification_config : PushNotificationConfig | None

Inherited members

class DeliveryMetrics (**data: Any)
Expand source code
class DeliveryMetrics(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    impressions: Annotated[
        StrictFloat | None, Field(description='Impressions delivered', ge=0.0)
    ] = None
    spend: Annotated[StrictFloat | None, Field(description='Amount spent', ge=0.0)] = None
    clicks: Annotated[StrictFloat | None, Field(description='Total clicks', ge=0.0)] = None
    ctr: Annotated[
        StrictFloat | None,
        Field(description='Click-through rate (clicks/impressions)', ge=0.0, le=1.0),
    ] = None
    views: Annotated[
        StrictFloat | None,
        Field(
            description="Content engagements counted toward the billable view threshold. For video this is a platform-defined view event (e.g., 30 seconds or video midpoint); for audio/podcast it is a stream start; for other formats it follows the pricing model's view definition. When the package uses CPV pricing, spend = views × rate.",
            ge=0.0,
        ),
    ] = None
    completed_views: Annotated[
        StrictFloat | 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[
        StrictFloat | None,
        Field(
            description='Completion rate (completed_views/impressions). Null indicates the metric is not applicable to this package/buy (e.g. completion rate on a non-video buy).',
            ge=0.0,
            le=1.0,
        ),
    ] = None
    conversions: Annotated[
        StrictFloat | None,
        Field(
            description='Total conversions attributed to this delivery. When by_event_type is present, this equals the sum of all by_event_type[].count entries.',
            ge=0.0,
        ),
    ] = None
    conversion_value: Annotated[
        StrictFloat | None,
        Field(
            description='Total monetary value of attributed conversions (in the reporting currency)',
            ge=0.0,
        ),
    ] = None
    commissionable_value: Annotated[
        StrictFloat | None,
        Field(
            description='Settled portion of attributed conversion value eligible for revenue-share commission, in the reporting currency. For revenue_share pricing, spend = round_currency(commissionable_value × commission_rate). This is distinct from conversion_value because taxes, shipping, discounts, returns, cancellations, or ineligible items may be excluded under the agreed commission basis.',
            ge=0.0,
        ),
    ] = None
    roas: Annotated[
        StrictFloat | None,
        Field(description='Return on ad spend (conversion_value / spend)', ge=0.0),
    ] = None
    cost_per_acquisition: Annotated[
        StrictFloat | None, Field(description='Cost per conversion (spend / conversions)', ge=0.0)
    ] = None
    new_to_brand_rate: Annotated[
        StrictFloat | None,
        Field(
            description='Fraction of `conversions` (transactions) from first-time brand buyers, 0 = none, 1 = all. For retail-media unit-volume tracking of first-time buyers, see `new_to_brand_units` (count, not rate).',
            ge=0.0,
            le=1.0,
        ),
    ] = None
    leads: Annotated[
        StrictFloat | None,
        Field(
            description="Leads generated (convenience alias for by_event_type where event_type='lead')",
            ge=0.0,
        ),
    ] = None
    incremental_sales_lift: Annotated[
        StrictFloat | None,
        Field(
            description="Incremental sales lift attributed to the campaign — sales above the control/holdout baseline. Reported as a fraction (0.15 = 15% lift) or as an absolute value depending on seller convention. The seller's `attribution_methodology` qualifier (typically `deterministic_purchase` or `modeled`) and `attribution_window` qualifier on the matching `committed_metrics` entry disambiguate the methodology and window.",
            ge=0.0,
        ),
    ] = None
    brand_lift: Annotated[
        StrictFloat | None,
        Field(
            description="Brand lift — measured change in a brand metric (awareness, consideration, favorability, purchase intent, or ad recall) attributed to the campaign. Typically panel-based or survey-based. Reported as a fraction (0.05 = 5% lift). **Multidimensional in production** — Kantar, Upwave, Cint, DV all report each dimension separately with its own sample size and confidence interval. The dimension flows through `qualifier.lift_dimension` on `committed_metrics` / `by_package[].metric_values` (`awareness` | `consideration` | `favorability` | `purchase_intent` | `ad_recall`); rows under different dimensions are different surveyed outcomes and must not be combined. Use `attribution_methodology: 'panel_based'` qualifier when the underlying methodology is a panel.",
            ge=0.0,
        ),
    ] = None
    foot_traffic: Annotated[
        StrictFloat | None,
        Field(
            description="Store visits attributed to ad exposure. Count of incremental visits over baseline. Typically uses location-data panel methodology (`attribution_methodology: 'panel_based'`) or deterministic loyalty-card match (`attribution_methodology: 'deterministic_purchase'`).",
            ge=0.0,
        ),
    ] = None
    conversion_lift: Annotated[
        StrictFloat | None,
        Field(
            description='Incremental conversions attributed to the campaign — conversions above the control/holdout baseline. Reported as a fraction (0.10 = 10% lift) or as an absolute count depending on seller convention. Distinct from `conversions` (raw count of attributed conversions); conversion_lift requires a control group and an incrementality methodology.',
            ge=0.0,
        ),
    ] = None
    brand_search_lift: Annotated[
        StrictFloat | None,
        Field(
            description='Lift in brand search query volume attributed to the campaign — measured via search-data partnerships (Google, Microsoft) or survey methodology. Reported as a fraction (0.20 = 20% lift in branded search).',
            ge=0.0,
        ),
    ] = None
    plays: Annotated[
        StrictFloat | None,
        Field(
            description="Number of times the ad creative was displayed or played on DOOH or broadcast inventory. Raw play count before any impression multiplier is applied. Mirrors `forecastable-metric.json`'s `plays` token for forecast↔delivery reconciliation. Distinct from `dooh_metrics.loop_plays` (scheduled-rotation count) and from `impressions` (multiplied audience figure).",
            ge=0.0,
        ),
    ] = None
    measurement_source: Annotated[
        str | None,
        Field(
            description="Third-party measurement provider whose data produced this row's audience numbers. Mirrors delivery-forecast.json's measurement_source so forecast and delivery reconcile on the same declaration — distinct from demographic_system, which specifies demographic notation. Makes measured-channel rows (radio, broadcast, OOH) self-describing: a reconciliation join can tie delivered numbers to the system that measured them without consulting out-of-band context. Lowercase slug format.",
            examples=[
                'nielsen',
                'nielsen_audio',
                'videoamp',
                'comscore',
                'geopath',
                'barb',
                'agf',
                'oztam',
                'kantar',
                'barc',
                'route',
                'rajar',
                'triton',
            ],
            max_length=64,
            pattern='^[a-z0-9_]+$',
        ),
    ] = None
    by_event_type: Annotated[
        list[ByEventTypeItem] | None,
        Field(
            description='Conversion metrics broken down by event type. Spend-derived metrics (ROAS, CPA) are only available at the package/totals level since spend cannot be attributed to individual event types.'
        ),
    ] = None
    grps: Annotated[
        StrictFloat | None, Field(description='Gross Rating Points delivered (for CPP)', ge=0.0)
    ] = None
    reach: Annotated[
        StrictFloat | None,
        Field(
            description='Unique reach in the units specified by reach_unit. When reach_unit is omitted, units are unspecified — do not compare reach values across packages or media buys without a common reach_unit. The measurement window for this value is declared in `reach_window`; when `reach_window` is omitted, the window is unspecified and buyers MUST NOT sum reach across reports (the value MAY be a daily snapshot, a cumulative total, or something else).',
            ge=0.0,
        ),
    ] = None
    reach_unit: Annotated[
        reach_unit_1.ReachUnit | None,
        Field(
            description='Unit of measurement for the reach field. Aligns with the reach_unit declared on optimization goals and delivery forecasts. Required when reach is present to enable cross-platform comparison.'
        ),
    ] = None
    reach_window: Annotated[
        ReachWindow | None,
        Field(
            description='Measurement window for the reported `reach` and `frequency` values in this row. Declares whether the values are a per-period snapshot, a trailing rolling window, or cumulative-to-date — without this declaration, buyers summing `reach` across rows (e.g., daily delivery reports) can silently double-count audiences. Sellers SHOULD populate this whenever `reach` is present.'
        ),
    ] = None
    frequency: Annotated[
        StrictFloat | None,
        Field(
            description="Average frequency per reach unit, measured over the window declared in `reach_window`. When `reach_unit` is 'households', this is average exposures per household; when 'accounts', per logged-in account; etc. When `reach_window` is omitted, the window is unspecified — buyers MUST NOT compare or average frequency values across rows.",
            ge=0.0,
        ),
    ] = None
    quartile_data: Annotated[
        QuartileData | None,
        Field(
            description="Audio/video quartile completion data. Null indicates the metric is not applicable to this package/buy (e.g. quartile data on a non-video buy). Individual quartiles are addressable via the leaf metric identities `quartile_25` (q1_views), `quartile_50` (q2_views), `quartile_75` (q3_views), and `quartile_100` (q4_views) for declaration, commitments, aggregates, and breakdown sorting; this object remains the canonical carrier of the values. Quartiles are player-fired events (VAST firstQuartile/midpoint/thirdQuartile/complete). `quartile_100` counts 100%-of-duration completions and is distinct from `completed_views`, which counts completions at the seller's billable view threshold (`view_duration_seconds`) when one is set."
        ),
    ] = None
    time_based_views: Annotated[
        list[TimeBasedView] | None,
        Field(
            description="Time-threshold video view counts. Each entry reports views that met a continuous duration threshold under a stated basis, rather than a completion percentage (percentage-based completion is quartile_data). Thresholds of 2 and 6 seconds are RECOMMENDED cross-platform reporting points; any seller-defined threshold is permitted. One entry per (threshold_seconds, basis) pair per reporting period — sellers MUST de-duplicate before emission and MUST NOT emit the same pair twice; buyers MAY treat duplicate pairs as a seller-side conformance bug. Entries under different bases are different metrics and MUST NOT be summed (see view-threshold-basis). Primarily an autoplay/skippable-video metric (social, olv, in-feed video); completion metrics remain the currency for lean-back CTV/cinema inventory. Distinct from `views` (the single billable-threshold scalar) and from `viewability.viewed_seconds` (average in-view duration, not a threshold count). Array entries are not individually sortable in breakdown sort_by. Disclosure-grade surface: (threshold_seconds, basis) is not part of the committed-metric qualifier vocabulary, so a `committed_metrics` entry for `time_based_views` contracts the array's presence, not specific thresholds."
        ),
    ] = None
    dooh_metrics: Annotated[
        DoohMetrics1 | None,
        Field(description='DOOH-specific metrics (only included for DOOH campaigns)'),
    ] = None
    ooh_metrics: Annotated[
        OohMetrics | None,
        Field(
            description='Classic (static) OOH metrics — printed bulletins, posters, transit, and street furniture (only included for ooh campaigns). Experimental in AdCP 3.2. Static units have no play event: the delivery number is a period-level modeled audience estimate whose methodology tier is declared in estimation_basis (provider identity rides the row-level measurement_source), and the settlement artifact is the posting record — it proves the posting period, not an airing.'
        ),
    ] = None
    viewability: Annotated[
        Viewability1 | None,
        Field(
            description="Viewability metrics. Viewable rate should be calculated as viewable_impressions / measurable_impressions (not total impressions), since some environments cannot measure viewability. Includes `viewed_seconds` — average in-view duration — plus optional percentile and histogram distributions over that duration; all three use the same `measurable_impressions` population and are governed by the same viewability threshold (`standard`). Sellers SHOULD include `standard` whenever measured viewability values are reported because MRC and GroupM rows are not interchangeable. The numeric leaves are addressable via the leaf metric identities `viewable_rate`, `viewable_impressions`, `measurable_impressions`, and `viewed_seconds` for declaration, commitments, aggregates, and breakdown sorting. The structured distribution carriers require explicit `viewed_seconds_percentiles` and `viewed_seconds_histogram` identities for declaration, commitment, and selection; they are not numeric aggregate rows or sort keys. This object remains the canonical carrier of every value. When a buy reports under more than one standard, contract a specific standard via the `viewability_standard` qualifier on `committed_metrics`; when the package's `committed_metrics` carry a `viewability_standard` qualifier, sellers MUST populate `standard` on reported viewability objects so reconciliation can match the qualifier."
        ),
    ] = None
    engagements: Annotated[
        StrictFloat | 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. Maps to DBCFM KPI_INTERACTIONS (Interaktionen) in the Reporting/Performance block.",
            ge=0.0,
        ),
    ] = None
    follows: Annotated[
        StrictFloat | None,
        Field(
            description='New followers, page likes, artist/podcast/channel follows, or free channel/feed subscribes attributed to this delivery. Paid subscriptions are conversion events with `event_type: subscribe`, not `follows`.',
            ge=0.0,
        ),
    ] = None
    saves: Annotated[
        StrictFloat | None,
        Field(
            description='Saves, bookmarks, playlist adds, pins attributed to this delivery.', ge=0.0
        ),
    ] = None
    profile_visits: Annotated[
        StrictFloat | 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
    engagement_rate: Annotated[
        StrictFloat | None,
        Field(
            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
    cost_per_click: Annotated[
        StrictFloat | None, Field(description='Cost per click (spend / clicks)', ge=0.0)
    ] = None
    cost_per_completed_view: Annotated[
        StrictFloat | None,
        Field(
            description="Cost per completed view (spend / completed_views). Primary CPCV pricing scalar for video/audio inventory; the package's `pricing_model` is `cpcv` when this field is the billing basis.",
            ge=0.0,
        ),
    ] = None
    cpm: Annotated[
        StrictFloat | None,
        Field(
            description="Cost per thousand impressions, computed as (spend / impressions) × 1000. Universal pricing scalar across CTV, display, mobile/web video, native, audio, and DOOH inventory; the package's `pricing_model` is `cpm` when this field is the billing basis. Field name aligns with the canonical `cpm` token in `enums/pricing-model.json` and `pricing-options/cpm-option.json` so buyers cross-walk pricing model → reported scalar without a translation table.",
            ge=0.0,
        ),
    ] = None
    downloads: Annotated[
        StrictFloat | None,
        Field(
            description="Audio/podcast downloads (IAB Podcast Measurement Technical Guidelines 2.x methodology). Distinct from `views` — for podcast inventory this is the count of podcast episode downloads; for streaming audio it is the count of stream starts that meet the platform's download threshold. Prefer this over `views` for audio inventory.",
            ge=0.0,
        ),
    ] = None
    units_sold: Annotated[
        StrictFloat | None,
        Field(
            description='Items sold attributed to this delivery. Retail-media scalar distinct from `conversions` — a single conversion (transaction) may carry multiple `units_sold`. Used by retail media platforms where the buyer optimizes against unit movement, not transaction count. Attribution lookback windows are platform-specific (commonly 7/14/30 days, view-through and click-through variants); sellers SHOULD declare the window via `reporting_capabilities.measurement_windows` or `measurement_terms` rather than encoding it in this scalar.',
            ge=0.0,
        ),
    ] = None
    new_to_brand_units: Annotated[
        StrictFloat | None,
        Field(
            description='Units sold to first-time brand buyers (count, not rate). Retail-media scalar — the unit-volume parallel to the conversion-fraction `new_to_brand_rate`. Used by retail media platforms where new-customer acquisition unit volume is a primary KPI. Same attribution-window note as `units_sold` applies.',
            ge=0.0,
        ),
    ] = None
    by_action_source: Annotated[
        list[ByActionSourceItem] | None,
        Field(
            description='Conversion metrics broken down by action source (website, app, in_store, etc.). Useful for omnichannel sellers where conversions occur across digital and physical channels.'
        ),
    ] = None
    vendor_metric_values: Annotated[
        list[vendor_metric_value.VendorMetricValue] | None,
        Field(
            description="Reported values for vendor-defined metrics that the product's `reporting_capabilities.vendor_metrics` declared. Each entry carries the vendor (BrandRef), the metric identifier within the vendor's vocabulary, the value, optional unit, and `measurable_impressions` as the coverage denominator — vendor measurement is rarely 100% of delivered impressions, since vendors only score impressions where their SDK fires or their panel matches. When a declared vendor metric is omitted from this array, buyers infer no measurement happened (no integration). One row per `(vendor.domain, vendor.brand_id, metric_id, qualifier)` per reporting period — the same vendor metric MAY appear in multiple rows only when each carries a distinct qualifier (e.g., 7-day and 30-day attribution windows); sellers MUST de-duplicate before emission and MUST NOT emit two rows with the same tuple; buyers MAY treat duplicate rows as a seller-side conformance bug. The structured `vendor_metric_values` array is the recommended path for vendor metrics; `additionalProperties: true` on this parent object is preserved so existing free-form vendor emissions remain conformant during migration."
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Subclasses

Class variables

var brand_lift : float | None
var brand_search_lift : float | None
var by_action_source : list[ByActionSourceItem] | None
var by_event_type : list[ByEventTypeItem] | None
var clicks : float | None
var commissionable_value : float | None
var completed_views : float | None
var completion_rate : float | None
var conversion_lift : float | None
var conversion_value : float | None
var conversions : float | None
var cost_per_acquisition : float | None
var cost_per_click : float | None
var cost_per_completed_view : float | None
var cpm : float | None
var ctr : float | None
var dooh_metrics : DoohMetrics1 | None
var downloads : float | None
var engagement_rate : float | None
var engagements : float | None
var follows : float | None
var foot_traffic : float | None
var frequency : float | None
var grps : float | None
var impressions : float | None
var incremental_sales_lift : float | None
var leads : float | None
var measurement_source : str | None
var model_config
var new_to_brand_rate : float | None
var new_to_brand_units : float | None
var ooh_metrics : OohMetrics | None
var plays : float | None
var profile_visits : float | None
var quartile_data : QuartileData | None
var reach : float | None
var reach_unit : ReachUnit | None
var reach_window : ReachWindow | None
var roas : float | None
var saves : float | None
var spend : float | None
var time_based_views : list[TimeBasedView] | None
var units_sold : float | None
var vendor_metric_values : list[VendorMetricValue] | None
var viewability : Viewability1 | None
var views : float | None

Inherited members

class DeliveryStatus (*args, **kwds)
Expand source code
class DeliveryStatus(StrEnum):
    delivering = 'delivering'
    not_delivering = 'not_delivering'
    completed = 'completed'
    budget_exhausted = 'budget_exhausted'
    flight_ended = 'flight_ended'
    goal_met = 'goal_met'

Enum where members are also (and must be) strings

Ancestors

  • enum.StrEnum
  • builtins.str
  • enum.ReprEnum
  • enum.Enum

Class variables

var budget_exhausted
var completed
var delivering
var flight_ended
var goal_met
var not_delivering
class DeliveryType (*args, **kwds)
Expand source code
class DeliveryType(StrEnum):
    guaranteed = 'guaranteed'
    non_guaranteed = 'non_guaranteed'

Enum where members are also (and must be) strings

Ancestors

  • enum.StrEnum
  • builtins.str
  • enum.ReprEnum
  • enum.Enum

Class variables

var guaranteed
var non_guaranteed
class FrequencyCap (**data: Any)
Expand source code
class FrequencyCap(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    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[
        StrictFloat | None,
        Field(
            deprecated=True,
            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
    max_impressions: Annotated[
        SchemaInt | None,
        Field(
            description="Maximum number of impressions per entity per window. For duration windows, implementations typically use a rolling window. campaign applies across the owning field's full flight: the package flight for a targeting overlay, or the MediaBuy flight for a root cap.",
            ge=1,
        ),
    ] = None
    per: Annotated[
        reach_unit.ReachUnit | None,
        Field(
            description='Entity granularity for impression counting. Required when max_impressions is set.'
        ),
    ] = 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

    @model_validator(mode='after')
    def _require_schema_required_group(self) -> FrequencyCap:
        # ``required`` asks whether the caller supplied the field, which is what
        # model_fields_set answers. An explicit null is a supplied value — on a
        # mutation input it is the command to clear — and a default the caller
        # never sent is not.
        for group in (('suppress',), ('suppress_minutes',), ('max_impressions',),):
            if all(name in self.model_fields_set for name in group):
                return self
        raise ValueError(
            'FrequencyCap requires at least one of these field groups: suppress | suppress_minutes | max_impressions'
        )

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Subclasses

Class variables

var max_impressions : int | None
var model_config
var per : ReachUnit | None
var suppress : Duration | None
var suppress_minutes : float | None
var window : Duration | None

Inherited members

class FrequencyCapScope (root: RootModelRootType = PydanticUndefined, **data)
Expand source code
class FrequencyCapScope(RootModel[Literal['package']]):
    root: Annotated[
        Literal['package'],
        Field(description='Scope for frequency cap application', title='Frequency Cap Scope'),
    ] = 'package'

Usage Documentation

RootModel and Custom Root Types

A Pydantic BaseModel for the root object of the model.

Attributes
-----=
root
The root object of the model.
__pydantic_root_model__
Whether the model is a RootModel.
__pydantic_private__
Private fields in the model.
__pydantic_extra__
Extra fields in the model.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

  • pydantic.root_model.RootModel[Literal['package']]
  • pydantic.root_model.RootModel
  • pydantic.main.BaseModel
  • typing.Generic

Class variables

var model_config
var root : Literal['package']
class GetMediaBuyArtifactsRequest (**data: Any)
Expand source code
class GetMediaBuyArtifactsRequest(AdcpRequest, AdcpVersionEnvelope):
    account: Annotated[
        account_ref.AccountReference | None,
        Field(
            description='Filter artifacts to a specific account. When omitted, returns artifacts across all accessible accounts.'
        ),
    ] = None
    media_buy_id: Annotated[str, Field(description='Media buy to get artifacts from')]
    package_ids: Annotated[
        list[str] | None,
        Field(description='Filter to specific packages within the media buy', min_length=1),
    ] = None
    failures_only: Annotated[
        StrictBool | None,
        Field(
            description="When true, only return artifacts where the seller's local model returned local_verdict: 'fail'. Useful for auditing false positives. Not useful when the seller does not run a local evaluation model (all verdicts are 'unevaluated')."
        ),
    ] = False
    time_range: Annotated[TimeRange | None, Field(description='Filter to specific time period')] = (
        None
    )
    pagination: Annotated[
        Pagination | None,
        Field(
            description='Pagination parameters. Uses higher limits than standard pagination because artifact result sets can be very large.'
        ),
    ] = None
    context: context_1.ContextObject | None = None
    ext: ext_1.ExtensionObject | None = None

The request message of a task in the pinned bundle's task registry.

A consumer holding one can resolve its account, decide at-most-once, echo its context and negotiate version – the whole transport-boundary job – before knowing which tool it is. issubclass(model, AdcpRequest) is the registration-time proof that a model is spec-derived rather than a hand-written parallel: a field test passes for a forged model, descent does not.

Each accessor returns the field's value, or None when this tool's schema declares no such field. Only 49 of the 87 request schemas declare an account and only 43 an idempotency_key, so asking the request is what replaces getattr(req, "account", None) against Any at the boundary.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var account : AccountReference1 | AccountReference2 | None
var context : ContextObject | None
var ext : ExtensionObject | None
var failures_only : bool | None
var media_buy_id : str
var model_config
var package_ids : list[str] | None
var pagination : Pagination | None
var time_range : TimeRange | None

Inherited members

class GetMediaBuyDeliveryRequest (**data: Any)
Expand source code
class GetMediaBuyDeliveryRequest(AdcpRequest, AdcpVersionEnvelope):
    model_config = ConfigDict(
        extra='allow',
    )
    account: Annotated[
        account_ref.AccountReference | None,
        Field(
            description='Filter delivery data to a specific account. When omitted, returns data across all accessible accounts.'
        ),
    ] = None
    media_buy_ids: Annotated[
        list[str] | None,
        Field(description='Array of media buy IDs to get delivery data for', min_length=1),
    ] = None
    reporting_revision_id: Annotated[
        str | None,
        Field(
            description='Exact immutable reporting revision to retrieve. This additive Reliable Reporting selector returns content bound to that revision, including its immutable row count, control totals, and content SHA-256. It is mutually exclusive with media-buy, date, metric, and breakdown selectors.',
            max_length=255,
            min_length=1,
            pattern='^[A-Za-z0-9_.:-]{1,255}$',
        ),
    ] = None
    pagination: Annotated[
        pagination_request.PaginationRequest | None,
        Field(
            description='Cursor pagination. With reporting_revision_id, pages immutable authoritative rows while all revision metadata repeats on every page.'
        ),
    ] = None
    status_filter: Annotated[
        media_buy_status.MediaBuyStatus | StatusFilter | None,
        Field(description='Filter by status. Can be a single status or array of statuses'),
    ] = None
    start_date: Annotated[
        str | None,
        Field(
            description="Inclusive start date for the reporting period (YYYY-MM-DD), as a calendar date in the reporting timezone: the reporting_capabilities.timezone of the products behind the in-scope packages. It is a UTC day only when that timezone is UTC. When omitted along with end_date, returns campaign lifetime data. Only accepted when the product's reporting_capabilities.date_range_support is 'date_range'. A date-bounded request whose in-scope packages span more than one reporting timezone MUST be rejected with VALIDATION_ERROR. The buyer narrows media_buy_ids to buys that share one reporting timezone, or omits both dates when a single buy's packages span reporting timezones.",
            pattern='^\\d{4}-\\d{2}-\\d{2}$',
        ),
    ] = None
    end_date: Annotated[
        str | None,
        Field(
            description="Exclusive end date for the reporting period (YYYY-MM-DD), as a calendar date in the same reporting timezone as start_date. Must be later than start_date. When omitted along with start_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}$',
        ),
    ] = None
    include_package_daily_breakdown: Annotated[
        StrictBool | 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
    requested_metrics: Annotated[
        list[available_metric.AvailableMetric] | None,
        Field(
            description="Optional list of metrics to include in the response. When omitted, all available metrics are included (unchanged behavior). Applies to every metrics-bearing object in the response: totals, by_package, daily and window slices, and breakdown rows. impressions and spend are always included regardless of this list, except that a legacy or externally created mixed-currency buy MUST omit monetary and money-derived values from media-buy and window totals, MUST omit daily_breakdown, and MUST report monetary values only on currency-qualified package rows (including window package rows). Requesting a leaf metric identity returns its canonical nested carrier — e.g. requesting viewable_rate returns the viewability object, requesting quartile_75 returns quartile_data — never a flat duplicate. Metrics requested but not available for this buy are omitted from the response without error; contract accountability is unchanged — missing_metrics still reconciles against committed_metrics, but sellers MUST NOT list a metric in missing_metrics when its absence is solely due to this narrowing. Must be a subset of the product's reporting_capabilities.available_metrics; values outside the declared set are ignored. Subset evaluation follows the container-subsumption rule in enums/available-metric.json. Sort is evaluated before narrowing: excluding a metric from this list never triggers the sort_by fallback, and breakdown rows may be ordered by a metric absent from the narrowed payload — the applied-sort echo still names it. Same narrowing semantics as reporting_webhook.requested_metrics, with one shape difference: this field requires at least one entry when present (omit it entirely for full payloads), while the webhook field permits an empty array with the same meaning as omission.",
            min_length=1,
        ),
    ] = None
    time_granularity: Annotated[
        reporting_frequency.ReportingFrequency | None,
        Field(
            description="Per-window slice granularity for the pull, using the same vocabulary as reporting_webhook.reporting_frequency. When set, the seller returns per-window delivery slices over the date range — useful for reconstructing data a buyer's webhook receiver missed, since the slice payload is shape-aligned with what reporting_webhook would have delivered for the same window. Capability-scoped: the value MUST be one of the seller's declared reporting_capabilities.windowed_pull_granularities; otherwise the seller MUST return UNSUPPORTED_GRANULARITY. When set to daily, weekly, monthly, or quarterly and the in-scope packages span more than one reporting timezone, the seller MUST return VALIDATION_ERROR. When omitted, behavior is unchanged (cumulative aggregates plus optional daily breakdowns per existing fields)."
        ),
    ] = None
    include_window_breakdown: Annotated[
        StrictBool | None,
        Field(
            description="When true, the response includes media_buy_deliveries[].windows[] — an array of per-window delivery slices over the date range at the requested time_granularity. Ignored when time_granularity is omitted. Each window's payload mirrors what reporting_webhook would have delivered for the same window, enabling lossless GET-path recovery for buyers who missed webhook fires. Omit or set false to reduce response size when only cumulative aggregates are needed."
        ),
    ] = False
    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
    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. For every requested dimension that the product declares supported, the seller MUST return the corresponding array (possibly empty) and its truncated flag. Metric-sorted dimensions also return their applied-sort echoes; demographic and property-grain arrays also return their suppressed flag. Spot uses aired_at ordering and has no sort echoes. Note: keyword, catalog_item, and creative breakdowns are returned automatically when the seller supports them; including their keys here is optional and upgrades them to this negotiated contract without changing the automatic default.'
        ),
    ] = None
    context: context_1.ContextObject | None = None
    ext: ext_1.ExtensionObject | None = None

    @model_validator(mode='after')
    def _validate_delivery_selector_mode(self) -> GetMediaBuyDeliveryRequest:
        if self.reporting_revision_id is not None:
            if self.model_fields_set.intersection(('media_buy_ids', 'start_date', 'end_date', 'status_filter', 'requested_metrics', 'reporting_dimensions', 'attribution_window', 'include_package_daily_breakdown', 'time_granularity', 'include_window_breakdown')):
                raise ValueError('exact revision requests forbid aggregate selectors, even false or null')
        elif self.pagination is not None:
            raise ValueError('pagination requires reporting_revision_id')
        return self

    @model_serializer(mode='wrap')
    def _serialize_delivery_selector_mode(self, handler: SerializerFunctionWrapHandler) -> dict[str, Any]:
        value: dict[str, Any] = handler(self)
        if self.reporting_revision_id is not None:
            for name in ('media_buy_ids', 'start_date', 'end_date', 'status_filter', 'requested_metrics', 'reporting_dimensions', 'attribution_window', 'include_package_daily_breakdown', 'time_granularity', 'include_window_breakdown'):
                if name not in self.model_fields_set:
                    value.pop(name, None)
        return value

The request message of a task in the pinned bundle's task registry.

A consumer holding one can resolve its account, decide at-most-once, echo its context and negotiate version – the whole transport-boundary job – before knowing which tool it is. issubclass(model, AdcpRequest) is the registration-time proof that a model is spec-derived rather than a hand-written parallel: a field test passes for a forged model, descent does not.

Each accessor returns the field's value, or None when this tool's schema declares no such field. Only 49 of the 87 request schemas declare an account and only 43 an idempotency_key, so asking the request is what replaces getattr(req, "account", None) against Any at the boundary.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var account : AccountReference1 | AccountReference2 | None
var attribution_window : AttributionWindow | None
var context : ContextObject | None
var end_date : str | None
var ext : ExtensionObject | None
var include_package_daily_breakdown : bool | None
var include_window_breakdown : bool | None
var media_buy_ids : list[str] | None
var model_config
var pagination : PaginationRequest | None
var reporting_dimensions : ReportingDimensions | None
var reporting_revision_id : str | None
var requested_metrics : list[AvailableMetric] | None
var start_date : str | None
var status_filter : MediaBuyStatus | StatusFilter | None
var time_granularity : ReportingFrequency | None

Inherited members

class GetMediaBuyDeliveryResponse (**data: Any)
Expand source code
class GetMediaBuyDeliveryResponse(_LegacyGetMediaBuyDeliveryResponse, CanonicalBoundaryModel):
    """Canonical media-buy delivery response."""

Canonical media-buy delivery response.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
class Results (**data: Any)
Expand source code
class GetMediaBuyDeliveryResponse(_LegacyGetMediaBuyDeliveryResponse, CanonicalBoundaryModel):
    """Canonical media-buy delivery response."""

Canonical media-buy delivery response.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members

class GetMediaBuysRequest (**data: Any)
Expand source code
class GetMediaBuysRequest(AdcpRequest, AdcpVersionEnvelope):
    model_config = ConfigDict(
        extra='allow',
    )
    account: Annotated[
        account_ref.AccountReference | None,
        Field(
            description='Account to retrieve media buys for. When omitted, returns data across all accessible accounts.'
        ),
    ] = None
    media_buy_ids: Annotated[
        list[str] | None,
        Field(
            description='Array of media buy IDs to retrieve. When omitted, returns a paginated set of accessible media buys matching status_filter.',
            min_length=1,
        ),
    ] = None
    status_filter: Annotated[
        media_buy_status.MediaBuyStatus | StatusFilter | None,
        Field(
            description='Filter by status. Can be a single status or array of statuses. Defaults to ["active"] when media_buy_ids is omitted. When media_buy_ids is provided, no implicit status filter is applied.'
        ),
    ] = None
    indicator_types: Annotated[
        list[indicator_type.IndicatorType] | None,
        Field(
            description='Return media buys with at least one matching current indicator on the media buy, a package, or a package–creative assignment. Values within this field use OR logic; this field composes with status_filter using AND logic. Buyers MUST NOT send this filter unless the seller advertises every requested type in get_adcp_capabilities.media_buy.supported_indicator_types. A seller MAY reject a request that violates this precondition with UNSUPPORTED_FEATURE rather than silently returning an unfiltered superset.',
            min_length=1,
        ),
    ] = None
    include_snapshot: Annotated[
        StrictBool | None,
        Field(
            description='When true, include a near-real-time delivery snapshot for each package. Snapshots reflect the latest available entity-level stats from the platform (e.g., updated every ~15 minutes on GAM, ~1 hour on batch-only platforms). The staleness_seconds field on each snapshot indicates data freshness. If a snapshot cannot be returned, package.snapshot_unavailable_reason explains why. Defaults to false.'
        ),
    ] = False
    include_history: Annotated[
        SchemaInt | None,
        Field(
            description='When present, include the last N revision history entries for each media buy (returns min(N, available entries)). Each entry contains revision number, timestamp, actor, and a summary of what changed. Omit or set to 0 to exclude history (default). Recommended: 5-10 for monitoring, 50+ for audit.',
            ge=0,
            le=1000,
        ),
    ] = 0
    include_webhook_activity: Annotated[
        StrictBool | None,
        Field(
            description="When true, each returned media buy includes a `webhook_activity` array describing recent delivery-report webhook fires for the calling principal. Used by buyer agents to verify whether a publisher actually fired against the buyer's registered endpoint and what the endpoint returned — closes the operator-ticket loop for webhook debugging. Scoped to the calling principal: a buyer sees only fires targeting its own endpoint, even when multiple principals share visibility into the same media buy. Defaults to false. See `webhook_activity_limit` for the per-buy cap."
        ),
    ] = False
    webhook_activity_limit: Annotated[
        SchemaInt | None,
        Field(
            description="Maximum number of webhook delivery records to return per media buy, ordered most-recent first. Ignored when `include_webhook_activity` is false. Sellers that surface webhook activity MUST retain records for at least 30 days from each record's `completed_at` (see `webhook_activity` description in the response schema for the `pending`-status carve-out); sellers unable to honor that floor MUST omit the field entirely rather than truncate. When a buy has more historical fires than the limit, only the most recent are returned — there is no cursor for older fires; this surface is a debug aid, not a full audit log.",
            ge=1,
            le=200,
        ),
    ] = 50
    pagination: Annotated[
        pagination_request.PaginationRequest | None,
        Field(
            description='Cursor-based pagination controls. Strongly recommended when querying broad scopes (for example, all active media buys in an account).'
        ),
    ] = None
    context: context_1.ContextObject | None = None
    ext: ext_1.ExtensionObject | None = None

The request message of a task in the pinned bundle's task registry.

A consumer holding one can resolve its account, decide at-most-once, echo its context and negotiate version – the whole transport-boundary job – before knowing which tool it is. issubclass(model, AdcpRequest) is the registration-time proof that a model is spec-derived rather than a hand-written parallel: a field test passes for a forged model, descent does not.

Each accessor returns the field's value, or None when this tool's schema declares no such field. Only 49 of the 87 request schemas declare an account and only 43 an idempotency_key, so asking the request is what replaces getattr(req, "account", None) against Any at the boundary.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var account : AccountReference1 | AccountReference2 | None
var context : ContextObject | None
var ext : ExtensionObject | None
var include_history : int | None
var include_snapshot : bool | None
var include_webhook_activity : bool | None
var indicator_types : list[IndicatorType] | None
var media_buy_ids : list[str] | None
var model_config
var pagination : PaginationRequest | None
var status_filter : MediaBuyStatus | StatusFilter | None
var webhook_activity_limit : int | None

Inherited members

class GetMediaBuysResponse (**data: Any)
Expand source code
class GetMediaBuysResponse(_LegacyGetMediaBuysResponse, CanonicalBoundaryModel):
    """Canonical media-buy listing; rows are canonical media buys."""

    media_buys: Sequence[MediaBuy]

Canonical media-buy listing; rows are canonical media buys.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var media_buys : Sequence[MediaBuy]
var model_config

Inherited members

class ListProductsRequest (**data: Any)
Expand source code
class ListProductsRequest(AdcpRequest, AdcpVersionEnvelope):
    model_config = ConfigDict(
        extra='forbid',
    )
    idempotency_key: Annotated[
        str | None,
        Field(
            description='Optional replay key accepted uniformly on read calls.',
            max_length=255,
            min_length=16,
            pattern='^[A-Za-z0-9_.:-]{16,255}$',
        ),
    ] = None
    context_id: Annotated[
        str | None,
        Field(
            description='MCP compatibility field: servers ignore this value; A2A uses transport-native Message/Task contextId.',
            min_length=1,
        ),
    ] = None
    context: context_1.ContextObject | None = None
    governance_context: Annotated[str | None, Field(max_length=4096, min_length=1)] = None
    push_notification_config: Annotated[
        push_notification_config_1.PushNotificationConfig | None,
        Field(
            description='Uniform per-call envelope field accepted for SDK compatibility. This does not register a wholesale feed subscription; durable product.* and wholesale_feed.bulk_change subscribers are registered through sync_accounts notification_configs.'
        ),
    ] = None
    account: Annotated[
        canonical_account_ref.CanonicalAccountReference | None,
        Field(
            description='Account scope for pricing and availability. A natural-key account is the single brand source and MUST NOT be combined with top-level brand.'
        ),
    ] = None
    brand: brand_key.BrandKey | None = None
    criteria: product_discovery_criteria.ProductDiscoveryCriteria | None = None
    fields: product_fields.ProductResponseFields | None = None
    cursor: Annotated[str | None, Field(min_length=1)] = None
    max_results: Annotated[SchemaInt | None, Field(ge=1, le=100)] = 25
    if_feed_version: Annotated[
        str | None,
        Field(
            description='Opaque feed version returned by a prior list_products response or wholesale product-feed webhook for the same cache scope and canonicalized selection. Used for repair and conditional reconciliation, not routine polling when webhooks are active.'
        ),
    ] = None
    if_pricing_version: Annotated[
        str | None,
        Field(
            description='Opaque pricing version returned by a prior list_products response. Valid only with if_feed_version.'
        ),
    ] = None

The request message of a task in the pinned bundle's task registry.

A consumer holding one can resolve its account, decide at-most-once, echo its context and negotiate version – the whole transport-boundary job – before knowing which tool it is. issubclass(model, AdcpRequest) is the registration-time proof that a model is spec-derived rather than a hand-written parallel: a field test passes for a forged model, descent does not.

Each accessor returns the field's value, or None when this tool's schema declares no such field. Only 49 of the 87 request schemas declare an account and only 43 an idempotency_key, so asking the request is what replaces getattr(req, "account", None) against Any at the boundary.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var account : CanonicalAccountReference1 | CanonicalAccountReference2 | None
var brand : BrandKey | None
var context : ContextObject | None
var context_id : str | None
var criteria : ProductDiscoveryCriteria | None
var cursor : str | None
var fields : ProductResponseFields | None
var governance_context : str | None
var idempotency_key : str | None
var if_feed_version : str | None
var if_pricing_version : str | None
var max_results : int | None
var model_config
var push_notification_config : PushNotificationConfig | None

Inherited members

class MediaBuy (**data: Any)
Expand source code
class MediaBuy(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    media_buy_id: Annotated[str, Field(description="Seller's unique identifier for the media buy")]
    name: Annotated[
        str | None,
        Field(
            description='Human-readable name for this media buy, shared by buyer and seller for trafficking UI display and operational communication. Sellers MUST include the persisted name on read surfaces such as get_media_buys when the media buy was created through AdCP with name. Sellers MAY omit name for media buys created outside AdCP or created without name. This display label is not an identifier or financial reference.',
            max_length=255,
            min_length=1,
            pattern='\\S',
        ),
    ] = None
    accepted_proposal_id: Annotated[
        str | None,
        Field(
            description='Current accepted commercial snapshot. Compact-lifecycle buyers pass this ID to refine_proposals after restart or handoff. Updated atomically when an amendment or negotiated cancellation is accepted.',
            max_length=255,
            min_length=1,
        ),
    ] = None
    accepted_proposal_terms_digest: Annotated[
        str | None,
        Field(
            description='Digest of the current accepted proposal commercial_terms, allowing buyers and governance agents to verify the recovered snapshot.',
            pattern='^sha256:[A-Za-z0-9_-]{43}$',
        ),
    ] = None
    account: Annotated[
        account_1.Account | None, Field(description='Account billed for this media buy')
    ] = None
    status: media_buy_status.MediaBuyStatus
    health: Annotated[
        media_buy_health.MediaBuyHealth | None,
        Field(
            description="Aggregate health based on open impairments[]. Orthogonal to status — a paused, pending, or active buy can each be impaired. Defaults to 'ok' when impairments[] is empty."
        ),
    ] = media_buy_health.MediaBuyHealth.ok
    impairments: Annotated[
        list[impairment.Impairment] | None,
        Field(
            description="Open impairments — upstream dependency state changes that affect delivery for at least one package on this buy. Empty when health is 'ok'. Sellers MUST add an entry on next sync/poll response after a referenced resource transitions to an offline state, and MUST remove the entry (flipping health to 'ok' when the array empties) when the resource returns to a serviceable state. Staleness budget: the snapshot MUST reflect the impairment within 5 minutes of impairment.observed_at regardless of buyer poll cadence — sellers cannot rely on rare buyer polls to defer write propagation. See impairment.coherence assertion for the cross-resource invariant."
        ),
    ] = None
    rejection_reason: Annotated[
        str | None,
        Field(
            description="Reason provided by the seller when status is 'rejected'. Present only when status is 'rejected'."
        ),
    ] = None
    confirmed_at: Annotated[
        AwareDatetime | None,
        Field(
            description='ISO 8601 timestamp when the seller committed to this media buy. May be null until seller commitment occurs in deferred/manual approval flows. Once populated, remains stable through later pause, resume, activation, completion, cancellation, and reporting transitions.'
        ),
    ]
    cancellation: Annotated[
        Cancellation | None,
        Field(description="Cancellation metadata. Present only when status is 'canceled'."),
    ] = None
    total_budget: Annotated[
        StrictFloat, Field(description='Hard aggregate lifetime budget amount', ge=0.0)
    ]
    daily_budget_cap: Annotated[
        StrictFloat | None,
        Field(
            description='Current hard aggregate spend ceiling per calendar day. Sellers MUST echo this whenever an aggregate daily cap is set. It bounds total media-buy spend without allocating or reserving spend for packages.',
            ge=0.0,
        ),
    ] = None
    frequency_cap: Annotated[
        media_buy_frequency_cap.MediaBuyFrequencyCap | None,
        Field(
            description='Current hard MediaBuy-level cap. Sellers MUST echo it whenever set. Its counter aggregates exposures across all participating packages; each package targeting_overlay.frequency_cap remains independently binding.'
        ),
    ] = None
    budget_cap_timezone: Annotated[
        str | None,
        Field(
            description='IANA timezone defining the shared calendar-day boundary for every aggregate and package daily cap on this media buy. Sellers MUST echo it whenever any daily cap is set.'
        ),
    ] = None
    currency: Annotated[
        str | None,
        Field(
            description="Single ISO 4217 denomination for total_budget, every package budget/minimum, and every canonical BiddingPolicy monetary field. Every package's selected pricing option MUST declare this currency; packages requiring another currency belong in a separate media buy.",
            pattern='^[A-Z]{3}$',
        ),
    ] = None
    budget_allocation: Annotated[
        budget_allocation_1.BudgetAllocation | None,
        Field(
            description='Accepted cross-package budget allocation configuration. Omitted means fixed allocation for legacy buys.'
        ),
    ] = None
    pacing: Annotated[
        pacing_1.Pacing | None,
        Field(
            description='Aggregate pacing strategy for total_budget across the media-buy flight.'
        ),
    ] = None
    bidding: Annotated[
        bidding_policy.BiddingPolicy | None,
        Field(
            description='Media-buy-authored bidding policy. This is the complete default inherited by packages that omit package.bidding; `{automatic:true}` is an explicit authored automatic policy. In seller-optimized mode, cost_per/roas bind to the primary budget_allocation optimization goal. In fixed mode, inherited cost_per requires compatible package primary-goal result units and inherited roas requires value-bearing primary goals. Monetary fields use media_buy.currency; every affected pricing option MUST declare the same currency. Package overrides are permitted only where advertised; conflicts MUST be rejected atomically with BIDDING_PLACEMENT_CONFLICT.'
        ),
    ] = None
    packages: Annotated[
        list[package.Package], Field(description='Array of packages within this media buy')
    ]
    context: Annotated[
        context_1.ContextObject | None,
        Field(
            description='Opaque media-buy-level correlation data echoed unchanged from the create_media_buy request. Sellers MUST include persisted context on read surfaces such as get_media_buys when the media buy was created through AdCP with context, so buyers can reconcile seller-assigned media_buy_id values with their own tracking state. Sellers MAY omit context for media buys created outside AdCP or created without context. Sellers MUST NOT parse this object for business logic.'
        ),
    ] = None
    invoice_recipient: Annotated[
        business_entity.BusinessEntity | None,
        Field(
            description="Per-buy override for who receives the invoice. When provided, the seller invoices this entity instead of the account's default billing_entity. The seller MUST validate the invoice recipient is authorized for this account. When governance_agents are configured, the seller MUST include invoice_recipient in the check_governance request."
        ),
    ] = None
    creative_deadline: Annotated[
        AwareDatetime | None, Field(description='ISO 8601 timestamp for creative upload deadline')
    ] = None
    revision: Annotated[
        SchemaInt,
        Field(
            description='Monotonically increasing optimistic concurrency token. Incremented on every mutating state change or update; reads, validation-only calls, and exact idempotency replays do not increment it. Callers SHOULD include this in update_media_buy requests intended to change state — when provided, sellers MUST reject with CONFLICT if the revision does not match the current value, and MUST enforce that comparison atomically with the write.',
            ge=1,
        ),
    ]
    created_at: Annotated[AwareDatetime | None, Field(description='Creation timestamp')] = None
    updated_at: Annotated[AwareDatetime | None, Field(description='Last update timestamp')] = None
    ext: ext_1.ExtensionObject | None = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var accepted_proposal_id : str | None
var accepted_proposal_terms_digest : str | None
var account : Account | None
var bidding : BiddingPolicy | None
var budget_allocation : BudgetAllocation1 | BudgetAllocation2 | None
var budget_cap_timezone : str | None
var cancellation : Cancellation | None
var confirmed_at : pydantic.types.AwareDatetime | None
var context : ContextObject | None
var created_at : pydantic.types.AwareDatetime | None
var creative_deadline : pydantic.types.AwareDatetime | None
var currency : str | None
var daily_budget_cap : float | None
var ext : ExtensionObject | None
var frequency_cap : MediaBuyFrequencyCap | None
var health : MediaBuyHealth | None
var impairments : list[Impairment] | None
var invoice_recipient : BusinessEntity | None
var media_buy_id : str
var model_config
var name : str | None
var pacing : Pacing | None
var packages : list[Package]
var rejection_reason : str | None
var revision : int
var status : MediaBuyStatus
var total_budget : float
var updated_at : pydantic.types.AwareDatetime | None

Inherited members

class MediaBuyActionMode (*args, **kwds)
Expand source code
class MediaBuyActionMode(StrEnum):
    self_serve = 'self_serve'
    conditional_self_serve = 'conditional_self_serve'
    seller_managed = 'seller_managed'
    requires_approval = 'requires_approval'

Enum where members are also (and must be) strings

Ancestors

  • enum.StrEnum
  • builtins.str
  • enum.ReprEnum
  • enum.Enum

Class variables

var conditional_self_serve
var requires_approval
var self_serve
var seller_managed
class MediaBuyAvailableAction (**data: Any)
Expand source code
class MediaBuyAvailableAction(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    action: Annotated[
        media_buy_available_action_id.MediaBuyAvailableActionId,
        Field(description='The action identifier.'),
    ]
    mode: Annotated[
        media_buy_action_mode.MediaBuyActionMode,
        Field(
            description='The single mode that applies right now on this buy for this action. Singular because the buy has a concrete state, exactly one mode applies. Buyer SDKs branch on this to decide whether to expect a synchronous response, conditional handling, or an asynchronous approval callback.'
        ),
    ]
    task: Annotated[
        Task | None,
        Field(
            description='Compact-lifecycle task for this resolved action: operational control, commercial refinement, or creative lifecycle mutation.'
        ),
    ] = None
    sla: Annotated[
        sla_window.SlaWindow | None,
        Field(
            description='Optional SLA commitment for this action on this buy. Absence means no commitment, not zero commitment.'
        ),
    ] = None
    change_term_id: media_buy_change_term_id.MediaBuyChangeTermId | None = None
    terms_ref: media_buy_legacy_terms_ref.MediaBuyTermsReference | None = None
    applicable_package_ids: Annotated[
        list[applicable_package_id.ApplicablePackageId] | None,
        Field(
            description='For a package-scoped action, the exact packages currently eligible. Omission means every relevant package. Root actions omit this field.',
            min_length=1,
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var action : MediaBuyValidAction | Literal['update_media_buy_frequency_cap']
var applicable_package_ids : list[ApplicablePackageId] | None
var change_term_id : MediaBuyChangeTermId | None
var mode : MediaBuyActionMode
var model_config
var sla : SlaWindow | None
var task : Task | None
var terms_ref : MediaBuyTermsReference | None

Inherited members

class MediaBuyChangeTerm (**data: Any)
Expand source code
class MediaBuyChangeTerm(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    term_id: Annotated[str, Field(pattern='^[A-Za-z0-9_.:-]+$')]
    action: canonical_media_buy_action.CanonicalMediaBuyActionName
    service_mode: canonical_media_buy_action_mode.CanonicalMediaBuyActionMode
    allowed_statuses: Annotated[
        list[AllowedStatus] | None,
        Field(
            description='Non-terminal MediaBuy statuses in which this negotiated right may be exercised. When absent, the right applies in every non-terminal status where the canonical action itself is meaningful. This field describes contractual lifecycle scope; available_actions[] remains authoritative for the current instant.',
            min_length=1,
        ),
    ] = None
    processing_sla: Annotated[
        sla_window.SlaWindow | None,
        Field(
            description='Binding elapsed-time acknowledgement and completion commitment. Sellers account for weekends and non-working periods when declaring the maximum.'
        ),
    ] = None
    conditions: Annotated[
        list[Condition] | None,
        Field(
            description='Opaque stable condition identifiers defined by terms_ref or bilateral commercial documentation. Implementations compare identifiers; they MUST NOT execute or interpret them as instructions.',
            min_length=1,
        ),
    ] = None
    constraints: Annotated[
        change_term_constraints.MediaBuyChangeTermConstraints | None,
        Field(
            description='Portable bounds that buyer and seller SDKs can preflight. Omission means no machine-readable bound was promised; opaque conditions remain unevaluated.'
        ),
    ] = None
    terms_ref: Annotated[
        str | None,
        Field(
            description="Stable contract reference. Resolving it cannot expand the typed right and MUST use the caller's normal authenticated contract-document path, never ambient seller credentials.",
            max_length=1000,
            min_length=1,
        ),
    ] = None
    description: Annotated[
        str | None,
        Field(
            description='Display-only summary; it cannot grant authority, add an action, or override typed fields.',
            max_length=1000,
            min_length=1,
        ),
    ] = None
    ext: ext_1.ExtensionObject | None = None

    @model_validator(mode='after')
    def _validate_constraint_action(self) -> MediaBuyChangeTerm:
        if self.constraints is None:
            return self
        kind = self.constraints.kind
        allowed = {
            'budget': {
                'increase_budget', 'decrease_budget', 'reallocate_budget',
                'update_budget_allocation', 'update_spend_target',
            },
            'flight': {'extend_flight', 'shorten_flight', 'update_flight_dates'},
            'package_count': {'add_packages', 'remove_packages'},
            'effective_timing': {'pause', 'resume', 'cancel'},
        }
        action = self.action.value
        if action not in allowed.get(kind, set()):
            raise ValueError('constraint kind is incompatible with action')
        return self

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var action : CanonicalMediaBuyActionName
var allowed_statuses : list[AllowedStatus] | None
var conditions : list[Condition] | None
var constraints : MediaBuyChangeTermConstraints1 | MediaBuyChangeTermConstraints2 | MediaBuyChangeTermConstraints3 | MediaBuyChangeTermConstraints4 | None
var description : str | None
var ext : ExtensionObject | None
var model_config
var processing_sla : SlaWindow | None
var service_mode : CanonicalMediaBuyActionMode
var term_id : str
var terms_ref : str | None

Inherited members

class BudgetChangeConstraints (**data: Any)
Expand source code
class MediaBuyChangeTermConstraints1(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    kind: Literal['budget'] = 'budget'
    max_delta_amount: Annotated[
        Money | None,
        Field(
            description='Maximum absolute amount by which the affected budget may change in the direction named by the action.'
        ),
    ] = None
    max_delta_percent: Annotated[
        StrictFloat | None,
        Field(
            description='Maximum percentage change relative to the current committed value. Values above 100 are valid for increases greater than the current value.',
            ge=0.0,
        ),
    ] = None
    min_result_amount: Annotated[
        Money | None, Field(description='Minimum resulting committed value after the change.')
    ] = None
    max_result_amount: Annotated[
        Money | None, Field(description='Maximum resulting committed value after the change.')
    ] = None


    @model_validator(mode='after')
    def _require_portable_bound(self) -> MediaBuyChangeTermConstraints1:
        if not any(getattr(self, name) is not None for name in ('max_delta_amount', 'max_delta_percent', 'min_result_amount', 'max_result_amount')):
            raise ValueError('at least one portable constraint bound is required')
        return self

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var kind : Literal['budget']
var max_delta_amount : Money | None
var max_delta_percent : float | None
var max_result_amount : Money | None
var min_result_amount : Money | None
var model_config

Inherited members

class FlightChangeConstraints (**data: Any)
Expand source code
class MediaBuyChangeTermConstraints2(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    kind: Literal['flight'] = 'flight'
    max_change: Annotated[
        duration.Duration | None,
        Field(
            description='Maximum extension, shortening, or shift in the direction named by the action.'
        ),
    ] = None
    earliest_result: Annotated[
        AwareDatetime | None,
        Field(description='Earliest resulting start or end timestamp accepted for this action.'),
    ] = None
    latest_result: Annotated[
        AwareDatetime | None,
        Field(description='Latest resulting start or end timestamp accepted for this action.'),
    ] = None
    minimum_notice: Annotated[
        duration.Duration | None,
        Field(
            description='Minimum elapsed notice before the requested flight change may take effect.'
        ),
    ] = None


    @model_validator(mode='after')
    def _require_portable_bound(self) -> MediaBuyChangeTermConstraints2:
        if not any(getattr(self, name) is not None for name in ('max_change', 'earliest_result', 'latest_result', 'minimum_notice')):
            raise ValueError('at least one portable constraint bound is required')
        return self

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var earliest_result : pydantic.types.AwareDatetime | None
var kind : Literal['flight']
var latest_result : pydantic.types.AwareDatetime | None
var max_change : Duration | None
var minimum_notice : Duration | None
var model_config

Inherited members

class PackageCountChangeConstraints (**data: Any)
Expand source code
class MediaBuyChangeTermConstraints3(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    kind: Literal['package_count'] = 'package_count'
    max_additions: Annotated[
        SchemaInt | None,
        Field(description='Maximum packages that may be added by one exercise of the right.', ge=0),
    ] = None
    max_removals: Annotated[
        SchemaInt | None,
        Field(
            description='Maximum packages that may be removed by one exercise of the right.', ge=0
        ),
    ] = None
    max_result_count: Annotated[
        SchemaInt | None, Field(description='Maximum active package count after the change.', ge=0)
    ] = None


    @model_validator(mode='after')
    def _require_portable_bound(self) -> MediaBuyChangeTermConstraints3:
        if not any(getattr(self, name) is not None for name in ('max_additions', 'max_removals', 'max_result_count')):
            raise ValueError('at least one portable constraint bound is required')
        return self

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var kind : Literal['package_count']
var max_additions : int | None
var max_removals : int | None
var max_result_count : int | None
var model_config

Inherited members

class EffectiveTimingChangeConstraints (**data: Any)
Expand source code
class MediaBuyChangeTermConstraints4(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    kind: Literal['effective_timing'] = 'effective_timing'
    minimum_notice: Annotated[
        duration.Duration | None,
        Field(
            description='Minimum elapsed notice before pause, resume, cancellation, or another operational action may take effect.'
        ),
    ] = None
    earliest_effective_at: AwareDatetime | None = None
    latest_effective_at: AwareDatetime | None = None


    @model_validator(mode='after')
    def _require_portable_bound(self) -> MediaBuyChangeTermConstraints4:
        if not any(getattr(self, name) is not None for name in ('minimum_notice', 'earliest_effective_at', 'latest_effective_at')):
            raise ValueError('at least one portable constraint bound is required')
        return self

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var earliest_effective_at : pydantic.types.AwareDatetime | None
var kind : Literal['effective_timing']
var latest_effective_at : pydantic.types.AwareDatetime | None
var minimum_notice : Duration | None
var model_config

Inherited members

class MediaBuyChangeTermId (value: Any = <object object>, *, root: Any = <object object>)
Expand source code
class MediaBuyChangeTermId(ScalarStr):
    __slots__ = ()
    _constraints = {'pattern': '^[A-Za-z0-9_.:-]+$'}
    _json_schema_extra = {
        'description': 'The accepted proposal change_terms[].term_id from which this current-state action projection was derived.',
        'title': 'Media Buy Change Term ID',
    }

A str generated from a JSON Schema string root.

Ancestors

  • adcp.types._scalar.ScalarStr
  • adcp.types._scalar._ScalarRoot
  • builtins.str
class MediaBuyDelivery (**data: Any)
Expand source code
class MediaBuyDelivery(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    media_buy_id: Annotated[str, Field(description="Seller's media buy identifier")]
    currency: Annotated[
        str | None,
        Field(
            description="ISO 4217 denomination for monetary values in this media-buy delivery row, including totals, package spend, and currency-denominated package rates. Sellers SHOULD populate this field whenever all monetary values in the row share one currency. For AdCP-authored buys it MUST equal the media-buy currency, and every by_package[].currency MUST equal it. For a legacy or externally created mixed-currency buy, omit this field, daily_breakdown, and all monetary or money-derived values from row and window totals; report those values only at package grain with each package's own currency. AdCP does not perform currency conversion.",
            pattern='^[A-Z]{3}$',
        ),
    ] = None
    status: Annotated[
        Status,
        Field(
            description='Current media buy status. Lifecycle states use the same taxonomy as media-buy-status (`pending_creatives`, `pending_start`, `active`, `paused`, `completed`, `rejected`, `canceled`). In webhook context, reporting_delayed indicates data temporarily unavailable. `pending` is accepted as a legacy alias for pending_start.'
        ),
    ]
    expected_availability: Annotated[
        AwareDatetime | None,
        Field(
            description='When delayed data is expected to be available (only present when status is reporting_delayed)'
        ),
    ] = None
    is_adjusted: Annotated[
        StrictBool | None,
        Field(
            description='Indicates this delivery contains updated data for a previously reported period. Buyer should replace previous period data with these totals.'
        ),
    ] = None
    is_final: Annotated[
        StrictBool | None,
        Field(
            description="Whether this row's delivery data is final for the reporting period. The row does not carry its own `measurement_window` — that lives on each `by_package[*]` entry. Reconciliation joins on per-package `measurement_window`; this row-level flag is a convenience roll-up. Sellers MUST NOT emit `is_final: true` at the row level unless every entry in `by_package` has `is_final: true` for the same `measurement_window` as the buy's `measurement_terms.billing_measurement.measurement_window` (or for the row's natural window when no `billing_measurement.measurement_window` is set). On any disagreement between row-level and package-level finality, package-level is authoritative. When true, the seller considers these numbers closed and is willing to invoice on them subject to `measurement_terms.billing_measurement`. When false, numbers may still move as measurement matures (broadcast C3 → C7) or processing completes (IVT scrubbing, dedup). When absent, the seller does not distinguish provisional from final at the row level — consult per-package `is_final`."
        ),
    ] = None
    finalized_at: Annotated[
        AwareDatetime | None,
        Field(
            description="ISO 8601 timestamp at which this row became final. Present only when `is_final: true`. Anchors the buyer's reconciliation and (when later defined) dispute-window clocks against the buy's `measurement_terms.billing_measurement`. Computed as the latest `finalized_at` across the row's packages for the reconciliation window."
        ),
    ] = None
    pricing_model: Annotated[
        pricing_model_1.PricingModel | None,
        Field(description='Pricing model used for this media buy'),
    ] = None
    pacing_index: Annotated[
        StrictFloat | None,
        Field(
            description='Aggregate media-buy delivery pace relative to the media-buy pacing plan (1.0 = on track, <1.0 = behind, >1.0 = ahead). This is the authoritative pacing signal for seller-optimized buys; package pacing indexes are subordinate diagnostics.',
            ge=0.0,
        ),
    ] = None
    totals: Totals
    by_package: Annotated[list[ByPackageItem], Field(description='Metrics broken down by package')]
    windows: Annotated[
        list[Window] | None,
        Field(
            description="Per-window delivery slices over the reporting period at the requested time_granularity. Only present when the request set time_granularity and include_window_breakdown: true. Each slice mirrors what reporting_webhook would have delivered for the same window — buyers who missed webhook fires can reconstruct identical data by reading this array. Slice rows are ordered by window_start ascending; consecutive rows are contiguous (each row's window_end equals the next row's window_start) and partition the requested date range at the chosen granularity. For a legacy or external mixed-currency media buy, monetary and money-derived values MUST be omitted from each window totals object and reported only in currency-qualified windows[].by_package rows. Sellers MUST exclude this field when time_granularity is omitted; when set, sellers MUST honor pulls at any granularity in reporting_capabilities.windowed_pull_granularities (otherwise return UNSUPPORTED_GRANULARITY). See snapshot-and-log Rule 4 for the two-paths-parity contract this surface anchors."
        ),
    ] = None
    daily_breakdown: Annotated[
        list[DailyBreakdownItem1] | None,
        Field(
            description="Day-by-day delivery for a media-buy row with one currency. Sellers MUST omit this aggregate breakdown for a legacy or externally created mixed-currency buy because these rows have no package currency field. Sellers MUST also omit this aggregate breakdown when the media buy's packages span more than one reporting timezone; package-level daily_breakdown remains, each in its own product's reporting timezone."
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var by_package : list[ByPackageItem]
var currency : str | None
var daily_breakdown : list[DailyBreakdownItem1] | None
var expected_availability : pydantic.types.AwareDatetime | None
var finalized_at : pydantic.types.AwareDatetime | None
var is_adjusted : bool | None
var is_final : bool | None
var media_buy_id : str
var model_config
var pacing_index : float | None
var pricing_model : PricingModel | None
var status : Status
var totals : Totals
var windows : list[Window] | None

Inherited members

class MediaBuyFeatures (**data: Any)
Expand source code
class MediaBuyFeatures(AdCPBaseModel):
    __pydantic_extra__: Dict[str, StrictBool]
    model_config = ConfigDict(
        extra='allow',
    )
    inline_creative_management: Annotated[
        StrictBool | None,
        Field(
            deprecated=True,
            description='Deprecated 3.x compatibility capability for creatives provided inline in create_media_buy and update_media_buy package payloads. buy_products, accept_proposal, and control_media_buy never accept inline creatives. New integrations use the dedicated creative lifecycle. Removed in 4.0.',
        ),
    ] = None
    property_list_filtering: Annotated[
        StrictBool | None,
        Field(
            description='Honors property_list parameter in get_products to filter results to buyer-approved properties'
        ),
    ] = None
    catalog_management: Annotated[
        StrictBool | None,
        Field(
            description='Supports sync_catalogs task for catalog feed management with platform review and approval'
        ),
    ] = None
    catalog_item_availability_updates: Annotated[
        StrictBool | None,
        Field(
            description='Supports buyer-pushed item_availability_updates and item_availability_queries on sync_catalogs for immediate suppression/restoration and current-state readback in buyer-managed catalogs. Seller declarations may be true only with catalog_management: true; buyer required_features filters may request this feature alone. Requests containing availability operations are synchronous and accept at most 1,000 combined update and query entries. A successful suppress covers selection, dynamic rendering, and cached or pre-generated creatives materialized from the item. Internal lineage MUST retain resolved_account_id, catalog_id, catalog_generation, and item_id. Static creatives supplied or promoted by the buyer without catalog lineage remain outside this automatic guarantee. Suppression persists across feed refreshes and ordinary upserts until restore, expires_at, or catalog deletion. A seller that does not declare true MUST reject availability operations with UNSUPPORTED_FEATURE before lookup or mutation and MUST NOT interpret them as discovery. This does not control seller-owned wholesale inventory or let restore bypass seller controls.'
        ),
    ] = None
    committed_metrics_supported: Annotated[
        StrictBool | None,
        Field(
            description="Seller has per-package snapshot infrastructure for the reporting contract. When true, the seller MUST populate `package.committed_metrics` on committed `create_media_buy` responses where `confirmed_at` is non-null, MUST omit `package.committed_metrics` while `confirmed_at` is null for a provisional buy, and MUST honor append-only mid-flight metric additions via `update_media_buy`. The unified `committed_metrics` array (per the metric-accountability design) covers both standard and vendor-defined metric entries, so a single flag is load-bearing. Buyers filtering on this flag are detecting 'this seller can stamp the reporting contract,' which closes the audit gap from PR #3510 where absence of `committed_metrics` was indistinguishable between 'didn't snapshot' and 'snapshot infrastructure not implemented.'"
        ),
    ] = None
    seller_optimized_budget: Annotated[
        StrictBool | None,
        Field(
            description="Supports the core seller-optimized shared-budget contract for budget_allocation.mode `seller_optimized`: one hard shared total_budget, seller allocation of that total across the buy's packages against budget_allocation.optimization_goals, media-buy-level pacing, and echo of the allocation configuration on buy read surfaces. Sellers declaring true MUST accept eligible explicit-package and proposal executions that use only these core controls and MUST enforce the aggregate budget. Core media-buy pacing: sellers declaring true MUST accept omitted media-buy pacing (which defaults to even when total_budget is present) and pacing `even` on seller-optimized buys; they MAY reject `asap` or `front_loaded` with `UNSUPPORTED_FEATURE` (error.field `pacing`) before any provider mutation, and MUST NOT silently coerce them to `even`. Package-level controls inside a seller-optimized buy are separate capabilities: package budget caps (seller_optimized_package_budgets), package minimum-spend targets (seller_optimized_min_spend_targets), and package pacing (seller_optimized_package_pacing). A seller declaring this feature but not one of those sub-capabilities MUST reject any request that would leave that package control on a seller-optimized buy with `UNSUPPORTED_FEATURE` before any over-subscription validation or provider mutation, and MUST NOT silently drop, soften, or coerce it. Over-subscription validation (`INVALID_REQUEST`) applies only to package controls the seller has declared; see seller_optimized_min_spend_targets. Product combinations may still be rejected when their currencies, optimization capabilities, pricing terms, or delivery constraints are incompatible. Sellers that do not declare this feature MUST reject any request carrying `budget_allocation.mode: 'seller_optimized'` with `UNSUPPORTED_FEATURE` before any provider mutation, and MUST NOT coerce the request to fixed allocation."
        ),
    ] = None
    seller_optimized_package_budgets: Annotated[
        StrictBool | None,
        Field(
            description='Honors package `budget` as an optional hard lifetime package spend cap inside a seller-optimized buy: packages[].budget and new_packages[].budget on create_media_buy and update_media_buy, purchases[].budget on buy_products, package budget controls on control_media_buy, and max_spend_percentage on seller-optimized proposal allocations. The cap is a ceiling, not a reserved or current allocation, and package caps may sum above total_budget. Meaningful only with seller_optimized_budget: true; seller declarations may be true only with seller_optimized_budget: true, while buyer required_features filters may request this feature alone. A seller that declares seller_optimized_budget without this feature MUST reject a request that would leave a package budget on a seller-optimized buy, including an allocation-mode switch that retains fixed-mode package budgets (the buyer clears them with null in the same atomic update), with `UNSUPPORTED_FEATURE` before any over-subscription validation or provider mutation, and MUST NOT issue seller-optimized proposals carrying max_spend_percentage. Does not govern fixed allocation, where package budgets remain required.'
        ),
    ] = None
    seller_optimized_min_spend_targets: Annotated[
        StrictBool | None,
        Field(
            description='Honors package `min_spend_target` as a soft lifetime minimum-spend target inside a seller-optimized buy: packages[].min_spend_target and new_packages[].min_spend_target on create_media_buy and update_media_buy, purchases[].min_spend_target on buy_products, package min_spend_target controls on control_media_buy, and min_spend_target_percentage on seller-optimized proposal allocations. The seller SHOULD attempt to deliver at least the target before allocating incremental spend elsewhere; it is not a billing or delivery guarantee. Meaningful only with seller_optimized_budget: true; seller declarations may be true only with seller_optimized_budget: true, while buyer required_features filters may request this feature alone. Sellers declaring this feature MUST reject package minimum-spend targets summing above total_budget with `INVALID_REQUEST` before mutation, and, when they also declare seller_optimized_package_budgets, MUST likewise reject a min_spend_target above its own package budget. A seller that declares seller_optimized_budget without this feature MUST reject a request carrying a numeric min_spend_target with `UNSUPPORTED_FEATURE` before any over-subscription validation or provider mutation, so an over-subscribed target sent to such a seller yields `UNSUPPORTED_FEATURE`, and MUST NOT issue seller-optimized proposals carrying min_spend_target_percentage.'
        ),
    ] = None
    seller_optimized_package_pacing: Annotated[
        StrictBool | None,
        Field(
            description='Honors package `pacing` as subordinate per-package pacing inside a seller-optimized buy, in addition to the media-buy-level pacing covered by seller_optimized_budget: packages[].pacing and new_packages[].pacing on create_media_buy and update_media_buy, purchases[].pacing on buy_products, package pacing controls on control_media_buy, and allocation pacing on seller-optimized proposals. Package pacing MUST NOT cause delivery to exceed aggregate media-buy pacing. Meaningful only with seller_optimized_budget: true; seller declarations may be true only with seller_optimized_budget: true, while buyer required_features filters may request this feature alone. Package pacing equal to the effective media-buy pacing adds no subordinate constraint and does not require this feature; buyers SHOULD omit package pacing on seller-optimized buys unless this feature is advertised. A seller that declares seller_optimized_budget without this feature MUST reject a request that would leave package pacing differing from media-buy pacing on a seller-optimized buy, including an allocation-mode switch that retains such fixed-mode package pacing (the buyer can align it in the same update), with `UNSUPPORTED_FEATURE` before any provider mutation, and MUST NOT issue seller-optimized proposals carrying allocation pacing. Does not govern package pacing in fixed allocation.'
        ),
    ] = None
    bidding_policy: Annotated[
        bidding_policy_capability.BiddingPolicyCapability | None,
        Field(
            description='Structured support for canonical bidding by authored scope, allocation context, mode, strength, and strength-qualified multi-field combination. Presence does not imply support for both scopes, both allocation modes, or every policy shape. Sellers MUST preserve every advertised semantic exactly and reject unadvertised policies rather than translating them.'
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var bidding_policy : BiddingPolicyCapability | None
var canonical_creatives : bool | None
var catalog_item_availability_updates : bool | None
var catalog_management : bool | None
var committed_metrics_supported : bool | None
var model_config
var property_list_filtering : bool | None
var seller_optimized_budget : bool | None
var seller_optimized_min_spend_targets : bool | None
var seller_optimized_package_budgets : bool | None
var seller_optimized_package_pacing : bool | None

Instance variables

var inline_creative_management : bool | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.

Inherited members

class MediaBuyStatus (*args, **kwds)
Expand source code
class MediaBuyStatus(StrEnum):
    pending_creatives = 'pending_creatives'
    pending_start = 'pending_start'
    active = 'active'
    paused = 'paused'
    completed = 'completed'
    rejected = 'rejected'
    canceled = 'canceled'

Enum where members are also (and must be) strings

Ancestors

  • enum.StrEnum
  • builtins.str
  • enum.ReprEnum
  • enum.Enum

Class variables

var active
var canceled
var completed
var paused
var pending_creatives
var pending_start
var rejected
class MediaBuyValidAction (*args, **kwds)
Expand source code
class MediaBuyValidAction(StrEnum):
    pause = 'pause'
    resume = 'resume'
    cancel = 'cancel'
    update_name = 'update_name'
    extend_flight = 'extend_flight'
    shorten_flight = 'shorten_flight'
    update_flight_dates = 'update_flight_dates'
    increase_budget = 'increase_budget'
    decrease_budget = 'decrease_budget'
    reallocate_budget = 'reallocate_budget'
    update_budget_allocation = 'update_budget_allocation'
    update_targeting = 'update_targeting'
    update_pacing = 'update_pacing'
    update_bidding = 'update_bidding'
    update_frequency_caps = 'update_frequency_caps'
    replace_creative = 'replace_creative'
    update_creative_assignments = 'update_creative_assignments'
    remove_creative = 'remove_creative'
    add_packages = 'add_packages'
    remove_packages = 'remove_packages'
    update_budget = 'update_budget'
    update_dates = 'update_dates'
    update_packages = 'update_packages'
    sync_creatives = 'sync_creatives'

Enum where members are also (and must be) strings

Ancestors

  • enum.StrEnum
  • builtins.str
  • enum.ReprEnum
  • enum.Enum

Class variables

var add_packages
var cancel
var decrease_budget
var extend_flight
var increase_budget
var pause
var reallocate_budget
var remove_creative
var remove_packages
var replace_creative
var resume
var shorten_flight
var sync_creatives
var update_bidding
var update_budget
var update_budget_allocation
var update_creative_assignments
var update_dates
var update_flight_dates
var update_frequency_caps
var update_name
var update_pacing
var update_packages
var update_targeting
class Overlay (**data: Any)
Expand source code
class Overlay(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    id: Annotated[
        str,
        Field(
            description="Identifier for this overlay (e.g., 'play_pause', 'volume', 'publisher_logo', 'carousel_prev', 'carousel_next')"
        ),
    ]
    description: Annotated[
        str | None,
        Field(
            description='Human-readable explanation of what this overlay is and how buyers should account for it'
        ),
    ] = None
    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
    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."
        ),
    ]

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var bounds : Bounds
var description : str | None
var id : str
var model_config
var visual : Visual | None

Inherited members

class Pacing (*args, **kwds)
Expand source code
class Pacing(StrEnum):
    even = 'even'
    asap = 'asap'
    front_loaded = 'front_loaded'

Enum where members are also (and must be) strings

Ancestors

  • enum.StrEnum
  • builtins.str
  • enum.ReprEnum
  • enum.Enum

Class variables

var asap
var even
var front_loaded
class MediaBuyPackage (**data: Any)
Expand source code
class Package(IndicatorBearingResourceState):
    model_config = ConfigDict(
        extra='allow',
    )
    indicator_types_evaluated: Annotated[
        list[IndicatorTypesEvaluatedEnum1] | None,
        Field(
            description='Indicator types covered by this snapshot. Required whenever indicators is present. Types omitted from this list remain unknown even when indicators is empty. Every returned indicator.type MUST appear in this list.',
            min_length=1,
        ),
    ] = None
    indicators: Annotated[
        list[Indicator1] | None,
        Field(
            description='Current seller assertions for the indicator types and publisher/placement coverage named by the sibling evaluation fields. Omitted means unknown or not evaluated. A present empty array means evaluated with no current assertion for indicator_types_evaluated in the evaluated scope.'
        ),
    ] = None
    package_id: Annotated[str, Field(description="Seller's package identifier")]
    product_id: Annotated[
        str | None,
        Field(
            description="Product identifier this package is purchased from. For packages created from an explicit create_media_buy package request, sellers MUST echo the request package's product_id on every response package object that represents that requested package."
        ),
    ] = None
    budget: Annotated[
        StrictFloat | None,
        Field(
            description='Hard lifetime package spend cap denominated in media_buy.currency. In seller-optimized mode this is not a current allocation.',
            ge=0.0,
        ),
    ] = None
    min_spend_target: Annotated[
        StrictFloat | None,
        Field(
            description='Accepted soft lifetime spend target for this package under seller-optimized allocation.',
            ge=0.0,
        ),
    ] = None
    daily_budget_cap: Annotated[
        StrictFloat | None,
        Field(
            description='Current hard package spend ceiling per shared media-buy cap day, denominated in media_buy.currency. It is a subordinate ceiling, not a reserved allocation; media_buy.budget_cap_timezone defines the day boundary.',
            ge=0.0,
        ),
    ] = None
    currency: Annotated[
        str | None,
        Field(
            description='Legacy/readback package denomination for buys created outside the canonical 3.2 path. For AdCP-authored media buys this MUST equal media_buy.currency; canonical package budget and BiddingPolicy values always use media_buy.currency. Snapshot currency may still identify externally reported spend denomination.',
            pattern='^[A-Z]{3}$',
        ),
    ] = None
    bid_price: Annotated[
        StrictFloat | None,
        Field(
            deprecated=True,
            description='DEPRECATED legacy bid representation. 3.2 sellers SHOULD normalize and echo package.bidding instead.',
            ge=0.0,
        ),
    ] = None
    bidding: Annotated[
        bidding_policy.BiddingPolicy | None,
        Field(
            description='Package-authored bidding override. `{automatic:true}` explicitly overrides media_buy.bidding with provider automatic delivery. Omitted when this package inherits; sellers MUST NOT copy an inherited block here. Monetary fields use media_buy.currency.'
        ),
    ] = None
    optimization_goals: Annotated[
        list[optimization_goal.OptimizationGoal] | None,
        Field(
            description='Current package objective functions. Currency-bearing execution controls are returned separately in package.bidding or inherited from media_buy.bidding.',
            min_length=1,
        ),
    ] = None
    format_ids: Annotated[
        list[format_id.FormatReferenceStructuredObject] | None,
        Field(
            deprecated=True,
            description='Deprecated in AdCP 3.2; removed in AdCP 4.0. Legacy named-format IDs supplied for this package on create_media_buy. Sellers SHOULD echo this field whenever the request included it, including dual-emission cases where another selector won precedence.',
            min_length=1,
        ),
    ] = None
    format_option_refs: Annotated[
        list[format_option_ref.FormatOptionReference] | None,
        Field(
            description='Structured 3.1+ format option references supplied for this package on create_media_buy. Sellers SHOULD echo this field whenever the request included it.',
            min_length=1,
        ),
    ] = None
    format_kind: Annotated[
        str | None,
        Field(
            description='Direct canonical selector supplied for this package on create_media_buy. Sellers SHOULD echo this field whenever the request included it, including informational-echo cases where another selector won precedence.'
        ),
    ] = None
    params: Annotated[
        dict[str, Any] | None,
        Field(
            description='Parameters for the direct canonical selector in `format_kind`, echoed from the create_media_buy request whenever the request included it. Requires `format_kind`.'
        ),
    ] = None
    impressions: Annotated[
        StrictFloat | None,
        Field(description='Goal impression count for impression-based packages', ge=0.0),
    ] = None
    pacing: Annotated[
        pacing_1.Pacing | None,
        Field(
            description='Package-level pacing preference. Under seller-optimized allocation this is subordinate to the media-buy aggregate pacing.'
        ),
    ] = None
    targeting_overlay: Annotated[
        targeting.TargetingOverlay | None,
        Field(
            description='Complete effective targeting applied to this package, including configured-product targeting and the most recent package-specific overlay. Sellers SHOULD echo persisted targeting so buyers can verify stored state without replaying requests. Sellers MUST echo geo_places and geo_places_exclude whenever either was persisted, including the exact applied system_version and normalized values, so buyers can audit catalog-backed targeting. Sellers using placement, property-list, or collection-list targeting MUST include the committed inventory selection here. placement_selection mode default SHOULD resolve to mode selected with committed refs when enumerable; collection_selection follows the same rule, materializing the committed selectors even when the selection was produced through collection_list references.'
        ),
    ] = None
    targeting_resolution: Annotated[
        package_targeting_resolution.PackageTargetingResolution | None,
        Field(
            description='Execution details for accepted package targeting. Sellers MUST include targeting_resolution.demographics whenever demographic targeting was requested or applied; its applied predicate and execution fields report the effective booked state.'
        ),
    ] = None
    start_time: Annotated[
        AwareDatetime | None,
        Field(
            description='ISO 8601 flight start time for this package. Use to determine whether the package is within its scheduled flight before interpreting delivery status.'
        ),
    ] = None
    end_time: Annotated[
        AwareDatetime | None, Field(description='ISO 8601 flight end time for this package')
    ] = None
    paused: Annotated[
        StrictBool | None,
        Field(description='Whether this package is currently paused by the buyer'),
    ] = None
    canceled: Annotated[
        StrictBool | None,
        Field(
            description='Whether this package has been canceled. Canceled packages stop delivery and cannot be reactivated.'
        ),
    ] = None
    cancellation: Annotated[
        Cancellation1 | None,
        Field(description='Cancellation metadata. Present only when canceled is true.'),
    ] = None
    creative_deadline: Annotated[
        AwareDatetime | None,
        Field(
            description="ISO 8601 timestamp for creative upload or change deadline for this package. After this deadline, creative changes are rejected. When absent, the media buy's creative_deadline applies."
        ),
    ] = None
    context: Annotated[
        context_1.ContextObject | None,
        Field(
            description='Opaque package-level correlation data echoed unchanged from the create_media_buy package request. Sellers MUST include persisted package context on read surfaces when the package was created through AdCP with context, so buyers can reconcile seller-assigned package_id values with their own line items; this is the legacy-safe fallback when an older seller did not echo product_id on the create response. Sellers MAY omit context for packages created outside AdCP or created without context. Sellers MUST NOT parse this object for business logic.'
        ),
    ] = None
    creative_approvals: Annotated[
        list[CreativeApproval] | None,
        Field(
            description='Approval status for each creative assigned to this package. Absent when no creatives have been assigned.'
        ),
    ] = None
    formats_to_provide: Annotated[
        list[package_format_snapshot.PackageFormatSnapshot] | None,
        Field(
            description='The immutable PackageFormatSnapshot checklist established for this package at booking time. Contract-bearing snapshots remain present after creative coverage is complete so readback, assignment, and serving never fall back to a mutable live Product declaration.',
            min_length=1,
        ),
    ] = None
    formats_pending: Annotated[
        list[package_format_snapshot.PackageFormatSnapshot] | None,
        Field(
            description='PackageFormatSnapshot entries from formats_to_provide that do not yet have creative coverage. Each entry MUST be canonically equal to the corresponding checklist snapshot and, when product_snapshot_digest is present, carry the identical digest. An empty emitted array means all requirements are covered; absence means readiness was not reported.'
        ),
    ] = None
    format_ids_to_provide: Annotated[
        list[format_id.FormatReferenceStructuredObject] | None,
        Field(
            deprecated=True,
            description='**DEPRECATED in 3.2.** Legacy named-format projection of formats_to_provide retained for older 3.x peers. New sellers emit canonical formats_to_provide declarations.',
        ),
    ] = None
    format_ids_pending: Annotated[
        list[format_id.FormatReferenceStructuredObject] | None,
        Field(
            deprecated=True,
            description='**DEPRECATED in 3.2.** Legacy named-format projection of formats_pending retained for older 3.x peers. New sellers emit canonical formats_pending declarations. An empty emitted array means every projected requirement is covered. Absence means legacy readiness was not reported and MUST NOT be interpreted as full coverage.',
        ),
    ] = None
    snapshot_unavailable_reason: Annotated[
        snapshot_unavailable_reason_1.SnapshotUnavailableReason | None,
        Field(
            description='Machine-readable reason the snapshot is omitted. Present only when include_snapshot was true and snapshot is unavailable for this package.'
        ),
    ] = None
    snapshot: Annotated[
        Snapshot | None,
        Field(
            description='Near-real-time delivery snapshot for this package. Only present when include_snapshot was true in the request. Represents the latest available entity-level stats from the platform — not billing-grade data.'
        ),
    ] = None
    ext: ext_1.ExtensionObject | None = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Subclasses

Class variables

var bid_price : float | None
var bidding : BiddingPolicy | None
var budget : float | None
var canceled : bool | None
var cancellation : Cancellation1 | None
var context : ContextObject | None
var creative_approvals : list[CreativeApproval] | None
var creative_deadline : pydantic.types.AwareDatetime | None
var currency : str | None
var daily_budget_cap : float | None
var end_time : pydantic.types.AwareDatetime | None
var ext : ExtensionObject | None
var format_ids : list[FormatReferenceStructuredObject] | None
var format_ids_pending : list[FormatReferenceStructuredObject] | None
var format_ids_to_provide : list[FormatReferenceStructuredObject] | None
var format_kind : str | None
var format_option_refs : list[FormatOptionReference1 | FormatOptionReference2] | None
var formats_pending : list[PackageFormatSnapshot18 | PackageFormatSnapshot19 | PackageFormatSnapshot20 | PackageFormatSnapshot21 | PackageFormatSnapshot22 | PackageFormatSnapshot23 | PackageFormatSnapshot24 | PackageFormatSnapshot25 | PackageFormatSnapshot26 | PackageFormatSnapshot27 | PackageFormatSnapshot28 | PackageFormatSnapshot29 | PackageFormatSnapshot30 | PackageFormatSnapshot31 | PackageFormatSnapshot32 | PackageFormatSnapshot33] | None
var formats_to_provide : list[PackageFormatSnapshot18 | PackageFormatSnapshot19 | PackageFormatSnapshot20 | PackageFormatSnapshot21 | PackageFormatSnapshot22 | PackageFormatSnapshot23 | PackageFormatSnapshot24 | PackageFormatSnapshot25 | PackageFormatSnapshot26 | PackageFormatSnapshot27 | PackageFormatSnapshot28 | PackageFormatSnapshot29 | PackageFormatSnapshot30 | PackageFormatSnapshot31 | PackageFormatSnapshot32 | PackageFormatSnapshot33] | None
var impressions : float | None
var indicator_types_evaluated : list[IndicatorTypesEvaluatedEnum1] | None
var indicators : list[Indicator1] | None
var min_spend_target : float | None
var model_config
var optimization_goals : list[OptimizationGoal8 | OptimizationGoal9 | OptimizationGoal10] | None
var pacing : Pacing | None
var package_id : str
var params : dict[str, typing.Any] | None
var paused : bool | None
var product_id : str | None
var snapshot : Snapshot | None
var snapshot_unavailable_reason : SnapshotUnavailableReason | None
var start_time : pydantic.types.AwareDatetime | None
var targeting_overlay : TargetingOverlay | None
var targeting_resolution : PackageTargetingResolution | None
class Package (**data: Any)
Expand source code
class Package(_LegacyPackage, CanonicalBoundaryModel):
    """Canonical package; legacy format identity is absent."""

    if TYPE_CHECKING:  # the removed fields, hidden from the constructor too
        format_ids: _RemovedFormatIds = Field(default=None, init=False)
        format_ids_pending: _RemovedFormatIds = Field(default=None, init=False)
        format_ids_to_provide: _RemovedFormatIds = Field(default=None, init=False)

Canonical package; legacy format identity is absent.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var format_ids : list[FormatReferenceStructuredObject] | None
var format_ids_pending : list[FormatReferenceStructuredObject] | None
var format_ids_to_provide : list[FormatReferenceStructuredObject] | None
var model_config

Instance variables

var bid_price : float | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.

Inherited members

class PackageRequest (**data: Any)
Expand source code
class PackageRequest(_LegacyPackageRequest, CanonicalBoundaryModel):
    """Canonical package request preserving beta.3 selector constraints."""

    if TYPE_CHECKING:  # the removed field, hidden from the constructor too
        format_ids: _RemovedFormatIds = Field(default=None, init=False)

    creatives: list[CreativeAsset] | None = Field(default=None, min_length=1)

    @model_validator(mode="after")
    def _validate_format_params(self) -> PackageRequest:
        if self.params is not None and self.format_kind is None:
            raise ValueError("params requires format_kind")
        if self.params is not None and self.format_kind == "image":
            if ("width" in self.params) != ("height" in self.params):
                raise ValueError("image params width and height must co-occur")
        return self

Canonical package request preserving beta.3 selector constraints.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var creatives : list[CreativeAsset] | None
var model_config
var targeting_overlay : TargetingOverlayInput | TargetingOverlay | None

Inherited members

class PackageUpdate (**data: Any)
Expand source code
class PackageUpdate(_LegacyPackageUpdate, CanonicalBoundaryModel):
    """Canonical package update; creatives are canonical assets."""

    creatives: list[CreativeAsset] | None = Field(default=None, min_length=1)  # type: ignore[assignment]

Canonical package update; creatives are canonical assets.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var creatives : list[CreativeAsset] | None
var model_config
var targeting_overlay : TargetingOverlayInput | TargetingOverlay | None

Inherited members

class ProductAllowedAction (**data: Any)
Expand source code
class ProductAllowedAction(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    action: Annotated[
        media_buy_available_action_id.MediaBuyAvailableActionId,
        Field(
            description='The action identifier. Accepts every legacy valid_actions value plus structured-only actions such as update_media_buy_frequency_cap.'
        ),
    ]
    modes: Annotated[
        list[media_buy_action_mode.MediaBuyActionMode],
        Field(
            description='Modes available for this action on this product. A product may declare multiple modes (for example `self_serve` within tolerances, escalating to `requires_approval` outside) — the buy-side `available_actions[<action>].mode` resolves to the singular mode in effect at mutation time. SDKs that see multiple modes MUST NOT assume which one will fire; they must read the resolved `mode` on the buy.',
            min_length=1,
        ),
    ]
    allowed_statuses: Annotated[
        list[media_buy_status.MediaBuyStatus] | None,
        Field(
            description='Media buy statuses in which this action is permitted. When absent, the action is permitted in all non-terminal statuses (`pending_creatives`, `pending_start`, `active`, `paused`).',
            min_length=1,
        ),
    ] = None
    sla: Annotated[
        sla_window.SlaWindow | None,
        Field(
            description='Optional SLA commitment for this action on this product. Absence means no commitment.'
        ),
    ] = None
    constraints: Annotated[
        change_term_constraints.MediaBuyChangeTermConstraints | None,
        Field(
            description='Optional advisory machine-readable bounds buyers can use during product selection. The proposal must restate any binding bounds in commercial_terms.change_terms[].constraints.'
        ),
    ] = None
    terms_ref: Annotated[
        str | None,
        Field(
            description='Optional advisory pointer to published commercial terms governing this product action. It is not a proposal change-term identity and never grants a binding change right; a proposal materializes binding rights under commercial_terms.change_terms[].term_id.'
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var action : MediaBuyValidAction | Literal['update_media_buy_frequency_cap']
var allowed_statuses : list[MediaBuyStatus] | None
var constraints : MediaBuyChangeTermConstraints1 | MediaBuyChangeTermConstraints2 | MediaBuyChangeTermConstraints3 | MediaBuyChangeTermConstraints4 | None
var model_config
var modes : list[MediaBuyActionMode]
var sla : SlaWindow | None
var terms_ref : str | None

Inherited members

class Proposal (**data: Any)
Expand source code
class Proposal(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    proposal_id: Annotated[
        str,
        Field(
            description='Unique identifier for this proposal. Used to finalize a draft proposal and to execute a committed proposal via create_media_buy.',
            max_length=255,
        ),
    ]
    name: Annotated[
        str, Field(description='Human-readable name for this media plan proposal', max_length=500)
    ]
    description: Annotated[
        str | None,
        Field(
            description='Explanation of the proposal strategy and what it achieves', max_length=2000
        ),
    ] = None
    allocations: Annotated[
        list[product_allocation.ProductAllocation],
        Field(
            description='Products and budget constraints in this plan. Fixed proposals require allocation_percentage on every entry and percentages MUST sum to 100. Seller-optimized proposals forbid exact allocation_percentage and may instead supply min_spend_target_percentage and max_spend_percentage, each only when the seller advertises the matching package-control capability (seller_optimized_min_spend_targets, seller_optimized_package_budgets). Publishers are responsible for validating cross-entry sums; buyers SHOULD validate them before execution.',
            min_length=1,
        ),
    ]
    budget_allocation: Annotated[
        budget_allocation_1.BudgetAllocation | None,
        Field(
            description='How the executed total budget is allocated across proposal products. Omit for legacy fixed proposals.'
        ),
    ] = None
    pacing: Annotated[
        pacing_1.Pacing | None,
        Field(
            description='Recommended aggregate pacing for the executed media-buy budget. On a committed proposal this is part of the firm delivery terms.'
        ),
    ] = None
    frequency_cap: Annotated[
        media_buy_frequency_cap.MediaBuyFrequencyCap | None,
        Field(
            description='Aggregate cap bound into this legacy proposal. It is authoritative when the proposal is executed and uses one counter across its packages.'
        ),
    ] = None
    proposal_status: Annotated[
        proposal_status_1.ProposalStatus | None,
        Field(
            description="Lifecycle status of this proposal and the per-proposal source of truth for whether finalization is required before create_media_buy. When absent, the proposal is ready to buy (backward compatible). 'draft' means indicative pricing — finalize via refine before purchasing. 'committed' means firm pricing with inventory reserved until expires_at and executable via create_media_buy."
        ),
    ] = None
    expires_at: Annotated[
        AwareDatetime | None,
        Field(
            description='When this proposal expires and can no longer be executed. For draft proposals, indicates when indicative pricing becomes stale. For committed proposals, indicates when the inventory hold lapses — the buyer must call create_media_buy before this time.'
        ),
    ] = None
    insertion_order: Annotated[
        insertion_order_1.InsertionOrder | None,
        Field(
            description='Formal insertion order attached to a committed proposal. Present when the seller requires a signed agreement before the media buy can proceed. The buyer references the io_id in io_acceptance on create_media_buy.'
        ),
    ] = None
    total_budget_guidance: Annotated[
        TotalBudgetGuidance | None, Field(description='Optional budget guidance for this proposal')
    ] = None
    brief_alignment: Annotated[
        str | None,
        Field(
            description='Explanation of how this proposal aligns with the campaign brief',
            max_length=2000,
        ),
    ] = None
    forecast: Annotated[
        delivery_forecast.DeliveryForecast | None,
        Field(
            description='Aggregate forecasted delivery metrics for the entire proposal. When both proposal-level and allocation-level forecasts are present, the proposal-level forecast is authoritative for total delivery estimation.'
        ),
    ] = None
    ext: ext_1.ExtensionObject | None = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var allocations : list[ProductAllocation]
var brief_alignment : str | None
var budget_allocation : BudgetAllocation1 | BudgetAllocation2 | None
var description : str | None
var expires_at : pydantic.types.AwareDatetime | None
var ext : ExtensionObject | None
var forecast : DeliveryForecast | None
var frequency_cap : MediaBuyFrequencyCap | None
var insertion_order : InsertionOrder | None
var model_config
var name : str
var pacing : Pacing | None
var proposal_id : str
var proposal_status : ProposalStatus | None
var total_budget_guidance : TotalBudgetGuidance | None

Inherited members

class RefineProposalsRequest (**data: Any)
Expand source code
class RefineProposalsRequest(AdcpRequest, AdcpVersionEnvelope):
    model_config = ConfigDict(
        extra='forbid',
    )
    context_id: Annotated[
        str | None,
        Field(
            description='MCP compatibility field: servers ignore this value; A2A uses transport-native Message/Task contextId.',
            min_length=1,
        ),
    ] = None
    context: context_1.ContextObject | None = None
    governance_context: Annotated[str | None, Field(max_length=4096, min_length=1)] = None
    push_notification_config: push_notification_config_1.PushNotificationConfig | None = None
    idempotency_key: Annotated[
        str,
        Field(
            description='Client-generated key required for retry-safe proposal refinement.',
            max_length=255,
            min_length=16,
            pattern='^[A-Za-z0-9_.:-]{16,255}$',
        ),
    ]
    refinements: Annotated[
        list[proposal_refinement.ProposalRefinement] | Refinements,
        Field(
            description='Proposal operations to apply, with at most 25 entries per request. revise creates a draft successor from a draft, committed, or accepted source. finalize MUST target a draft, reserves inventory, and creates a committed successor whose expires_at is the hold deadline. A batch containing finalize MUST contain only finalize entries and is atomic. proposal_id values MUST be unique; results preserve request order.',
            max_length=25,
            min_length=1,
        ),
    ]

The request message of a task in the pinned bundle's task registry.

A consumer holding one can resolve its account, decide at-most-once, echo its context and negotiate version – the whole transport-boundary job – before knowing which tool it is. issubclass(model, AdcpRequest) is the registration-time proof that a model is spec-derived rather than a hand-written parallel: a field test passes for a forged model, descent does not.

Each accessor returns the field's value, or None when this tool's schema declares no such field. Only 49 of the 87 request schemas declare an account and only 43 an idempotency_key, so asking the request is what replaces getattr(req, "account", None) against Any at the boundary.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var context : ContextObject | None
var context_id : str | None
var governance_context : str | None
var idempotency_key : str
var model_config
var push_notification_config : PushNotificationConfig | None
var refinements : list[ProposalRefinement2 | ProposalRefinement3 | ProposalRefinement4 | ProposalRefinement5 | ProposalRefinement6 | ProposalRefinement7 | ProposalRefinement8 | ProposalRefinement9] | Refinements

Inherited members

class RegistryAcceptancePolicyProfileReference (**data: Any)
Expand source code
class RegistryAcceptancePolicyProfileReference(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    policy_id: Annotated[str, Field(min_length=1)]
    policy_version: Annotated[str, Field(min_length=1)]
    policy_digest: Annotated[str, Field(pattern='^sha256:[a-f0-9]{64}$')]
    profile_id: Annotated[str, Field(pattern='^[A-Za-z0-9_.:-]+$')]
    profile_version: Annotated[str, Field(min_length=1)]
    profile_digest: Annotated[str, Field(pattern='^sha256:[a-f0-9]{64}$')]

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var policy_digest : str
var policy_id : str
var policy_version : str
var profile_digest : str
var profile_id : str
var profile_version : str

Inherited members

class RequestProposalsRequest (**data: Any)
Expand source code
class RequestProposalsRequest(AdcpRequest, AdcpVersionEnvelope):
    model_config = ConfigDict(
        extra='forbid',
    )
    context_id: Annotated[
        str | None,
        Field(
            description='MCP compatibility field: servers ignore this value; A2A uses transport-native Message/Task contextId.',
            min_length=1,
        ),
    ] = None
    context: context_1.ContextObject | None = None
    governance_context: Annotated[str | None, Field(max_length=4096, min_length=1)] = None
    push_notification_config: push_notification_config_1.PushNotificationConfig | None = None
    idempotency_key: Annotated[
        str,
        Field(
            description='Client-generated key required for retry-safe proposal creation.',
            max_length=255,
            min_length=16,
            pattern='^[A-Za-z0-9_.:-]{16,255}$',
        ),
    ]
    account: Annotated[
        canonical_account_ref.CanonicalAccountReference | None,
        Field(
            description='Alternative brand source for proposal terms. Provide either this natural-key account containing brand and operator or top-level brand, not both.'
        ),
    ] = None
    brand: Annotated[
        brand_key.BrandKey | None,
        Field(
            description='Alternative brand source for proposal terms. Provide either top-level brand or a natural-key account containing brand and operator, not both.'
        ),
    ] = None
    brief: Annotated[
        str,
        Field(
            description='Campaign goal, strategy, and requirements that are not represented in structured criteria.',
            min_length=1,
        ),
    ]
    criteria: product_discovery_criteria.ProductDiscoveryCriteria | None = None
    opportunity: Annotated[
        Opportunity | None,
        Field(
            description='Optional planning-cycle context that the seller associates with every proposal created by this request.'
        ),
    ] = None

    @model_validator(mode='after')
    def _require_schema_required_group(self) -> RequestProposalsRequest:
        # ``required`` asks whether the caller supplied the field, which is what
        # model_fields_set answers. An explicit null is a supplied value — on a
        # mutation input it is the command to clear — and a default the caller
        # never sent is not.
        for group in (('brand',), ('account',),):
            if all(name in self.model_fields_set for name in group):
                return self
        raise ValueError(
            'RequestProposalsRequest requires at least one of these field groups: brand | account'
        )

The request message of a task in the pinned bundle's task registry.

A consumer holding one can resolve its account, decide at-most-once, echo its context and negotiate version – the whole transport-boundary job – before knowing which tool it is. issubclass(model, AdcpRequest) is the registration-time proof that a model is spec-derived rather than a hand-written parallel: a field test passes for a forged model, descent does not.

Each accessor returns the field's value, or None when this tool's schema declares no such field. Only 49 of the 87 request schemas declare an account and only 43 an idempotency_key, so asking the request is what replaces getattr(req, "account", None) against Any at the boundary.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var account : CanonicalAccountReference1 | CanonicalAccountReference2 | None
var brand : BrandKey | None
var brief : str
var context : ContextObject | None
var context_id : str | None
var criteria : ProductDiscoveryCriteria | None
var governance_context : str | None
var idempotency_key : str
var model_config
var opportunity : Opportunity | None
var push_notification_config : PushNotificationConfig | None

Inherited members

class MediaBuyDeliveryStatus (*args, **kwds)
Expand source code
class Status(StrEnum):
    pending_creatives = 'pending_creatives'
    pending_start = 'pending_start'
    pending = 'pending'
    active = 'active'
    paused = 'paused'
    completed = 'completed'
    rejected = 'rejected'
    canceled = 'canceled'
    failed = 'failed'
    reporting_delayed = 'reporting_delayed'

Enum where members are also (and must be) strings

Ancestors

  • enum.StrEnum
  • builtins.str
  • enum.ReprEnum
  • enum.Enum

Class variables

var active
var canceled
var completed
var failed
var paused
var pending
var pending_creatives
var pending_start
var rejected
var reporting_delayed
class TargetingOverlay (**data: Any)
Expand source code
class TargetingOverlay(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    geo_countries: Annotated[
        list[GeoCountry] | None,
        Field(
            description="Restrict delivery to specific countries. ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE').",
            min_length=1,
        ),
    ] = None
    geo_countries_exclude: Annotated[
        Sequence[GeoCountriesExcludeItem] | None,
        Field(
            description="Exclude specific countries from delivery. ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE').",
            min_length=1,
        ),
    ] = None
    geo_regions: Annotated[
        list[GeoRegion] | None,
        Field(
            description='Restrict delivery to exact canonical ISO 3166-2 subdivisions (states, provinces, regions, departments, or other subdivision categories). Unknown identifiers are invalid. At create or update, sellers MUST reject unsupported identifiers and MUST NOT silently widen, drop, or partially apply the list. During get_products, a seller may instead return a sparse, buyer-reviewable targeting_resolution modification for a valid but unsupported requested outcome. Exact internal translation preserves accepted identifiers in package readback.',
            min_length=1,
        ),
    ] = None
    geo_regions_exclude: Annotated[
        Sequence[GeoRegionsExcludeItem] | None,
        Field(
            description='Exclude exact canonical ISO 3166-2 subdivisions. Support is independent from geo_regions inclusion support. Unknown identifiers and values also present in geo_regions are invalid. At create or update, sellers MUST reject unsupported identifiers and partial application; during get_products, a seller may instead return a sparse, buyer-reviewable targeting_resolution modification for a valid but unsupported requested outcome.',
            min_length=1,
        ),
    ] = None
    geo_metros: Annotated[
        list[geo_metro.GeoMetro] | None,
        Field(
            description='Restrict delivery to specific metro areas. Each entry specifies the classification system and target values. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
            title='Targeting Geo Metros',
        ),
    ] = None
    geo_metros_exclude: Annotated[
        Sequence[GeoMetrosExcludeItem] | None,
        Field(
            description='Exclude specific metro areas from delivery. Each entry specifies the classification system and excluded values. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_postal_areas: Annotated[
        list[postal_area.PostalArea] | None,
        Field(
            description='Restrict delivery to specific postal areas. Prefer the native country + postal system form. The deprecated legacy country-fused postal-system tokens remain accepted for compatibility. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_postal_areas_exclude: Annotated[
        Sequence[postal_area.PostalArea] | None,
        Field(
            description='Exclude specific postal areas from delivery. Prefer the native country + postal system form. The deprecated legacy country-fused postal-system tokens remain accepted for compatibility. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_places: Annotated[
        list[geo_place_area.GeographicPlaceArea] | None,
        Field(
            description='Restrict delivery to catalog-backed named places. Values MUST be stable identifiers in the declared system, not display names. Sellers must declare supported systems, countries, and place types in get_adcp_capabilities and reject unsupported entries rather than silently dropping them.',
            min_length=1,
        ),
    ] = None
    geo_places_exclude: Annotated[
        list[geo_place_area.GeographicPlaceArea] | None,
        Field(
            description='Exclude catalog-backed named places. Uses the same identifier-based shape as geo_places. Sellers MUST reject overlap with geo_places for the same country, system, place_type, and value.',
            min_length=1,
        ),
    ] = None
    daypart_targets: Annotated[
        list[daypart_target.DaypartTarget] | None,
        Field(
            description='Restrict delivery to specific time windows. Each entry specifies days of week, an hour range, and an optional timezone that defaults to inventory_local. A concrete IANA zone uses one shared civil-time clock, while inventory_local evaluates each inventory unit in its seller-assigned local timezone. Entries are independent and MAY use different clocks.',
            min_length=1,
        ),
    ] = None
    axe_include_segment: Annotated[
        str | None,
        Field(
            deprecated=True,
            description='Deprecated: Use TMP provider fields instead. AXE segment ID to include for targeting.',
        ),
    ] = None
    axe_exclude_segment: Annotated[
        str | None,
        Field(
            deprecated=True,
            description='Deprecated: Use TMP provider fields instead. AXE segment ID to exclude from targeting.',
        ),
    ] = None
    audience_include: Annotated[
        list[str] | None,
        Field(
            description='Restrict delivery to members of these first-party CRM audiences. Only users present in the uploaded lists are eligible. References audience_id values from sync_audiences on the same seller account — audience IDs are not portable across sellers. Not for lookalike expansion — express that intent in the campaign brief. Seller must declare support in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    audience_exclude: Annotated[
        list[str] | None,
        Field(
            description='Suppress delivery to members of these first-party CRM audiences. Matched users are excluded regardless of other targeting. References audience_id values from sync_audiences on the same seller account — audience IDs are not portable across sellers. Seller must declare support in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    signal_targeting_groups: Annotated[
        package_signal_targeting_groups.PackageSignalTargetingGroups | None,
        Field(
            description="Basic Boolean grouping for seller-offered signals. v1 supports a required top-level operator 'all' and child groups with operator 'any' for include groups or 'none' for exclusion groups. Example semantics: group 1 any(A, B) plus group 2 none(C, D) means (A OR B) AND NOT (C OR D). Signal entries reference named signal definitions with signal_ref scope 'product' for product-local signal options or scope 'data_provider' for external signals published in adagents.json signals[]. For simple include-only targeting, send one child group with operator 'any'. Sellers SHOULD reject entries that are not available for the product through inline signal_targeting_options or get_signals, are not active for the account, or exceed the product's signal_targeting_allowed/signal_targeting_rules/product terms. Signal targeting limits are product-scoped, not declared in get_adcp_capabilities, because products may be backed by different ad servers. Sellers MUST echo applied signal_targeting_groups on the resulting package state, including fixed/default selections. Sellers MAY return REQUOTE_REQUIRED when a targeting mutation changes commercial terms.",
            title='Targeting Signal Groups',
        ),
    ] = None
    signal_targeting: Annotated[
        list[signal_targeting_1.SignalTargeting] | None,
        Field(
            deprecated=True,
            description='DEPRECATED. Use signal_targeting_groups for package-level signal targeting. Legacy flat signal_targeting remains accepted during the SignalRef migration window but cannot express grouped include/exclude composition or product-scoped pricing.',
            min_length=1,
        ),
    ] = None
    demographics: Annotated[
        demographic_targeting_intent.DemographicTargetingIntent | None,
        Field(
            description='Canonical demographic audience targeting intent with optional constraints on how age may be determined. This is distinct from age_restriction: demographics selects an audience, while age_restriction expresses a legal eligibility or verification floor. Fresh create/update targeting MUST compile exactly or be rejected. During get_products, a seller may offer a different configured predicate only through sparse targeting_resolution modifications on a distinguishable product_id; selecting that product accepts the alternative. Sellers never silently broaden, narrow, default, drop, or substitute the basis.'
        ),
    ] = None
    frequency_cap: Annotated[
        frequency_cap_1.FrequencyCap | None, Field(title='Targeting Frequency Cap')
    ] = None
    property_list: Annotated[
        property_list_ref.PropertyListReference | None,
        Field(
            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.",
            title='Targeting Property List',
        ),
    ] = None
    property_list_exclude: Annotated[
        property_list_ref.PropertyListReference | None,
        Field(
            description="Reference to a property list whose properties must not carry the buyer's ads. Matched properties are removed from delivery. Use for brand-safety do-not-run lists (apps, sites). Exclude wins on overlap with property_list, and applies regardless of the product's property_targeting_allowed flag. Seller must declare support in get_adcp_capabilities."
        ),
    ] = None
    collection_list: Annotated[
        collection_list_ref.CollectionListReference | None,
        Field(
            description='Reference to a collection list for including specific collections (programs, publications, channels) within this product. The package runs on the intersection of matched collections and this list. Use for inclusion-based collection targeting. Seller must declare support in get_adcp_capabilities.',
            title='Targeting Collection List',
        ),
    ] = None
    collection_list_exclude: Annotated[
        collection_list_ref.CollectionListReference | None,
        Field(
            description="Reference to a collection list for excluding specific collections (programs, publications, channels) from this product. Matched collections must not carry the buyer's ads. Use for brand safety do-not-air lists. Seller must declare support in get_adcp_capabilities."
        ),
    ] = None
    placement_selection: Annotated[
        placement_selection_1.PlacementSelection | None,
        Field(
            description='Purchased placement selection within the product. This constrains package inventory; it is distinct from creative_assignments[].placement_refs, which only route individual creatives within the purchased set. On create, mode selected supplies the complete selected set and mode default uses the product default. In request-side Targeting Input, a non-null value replaces this dimension, omission preserves or inherits it, and null clears it when the product permits that broader inventory set.'
        ),
    ] = None
    collection_selection: Annotated[
        collection_selection_1.CollectionSelection | None,
        Field(
            description="Purchased collection selection within the product. On create, mode selected supplies the complete selected set and mode default uses the product's full bundle. On package readback this is the committed selection sellers MUST echo as concrete selectors, materializing any collection_list composition; collection_list fields remain the buyer-managed list mechanism. In request-side Targeting Input, a non-null value replaces this dimension, omission preserves or inherits it, and null clears it when the product permits that broader inventory set.",
            title='Targeting Collection Selection',
        ),
    ] = None
    age_restriction: Annotated[
        AgeRestriction | None,
        Field(
            description='Age restriction for compliance. Use for legal requirements (alcohol, gambling), not audience targeting.'
        ),
    ] = None
    device_platform: Annotated[
        list[device_platform_1.DevicePlatform] | None,
        Field(
            description='Restrict to specific platforms. Use for technical compatibility (app only works on iOS). Values from Sec-CH-UA-Platform standard, extended for CTV.',
            min_length=1,
        ),
    ] = None
    device_platform_exclude: Annotated[
        list[device_platform_1.DevicePlatform] | None,
        Field(
            description='Exclude specific operating-system platforms from delivery. When a platform appears in both device_platform and device_platform_exclude, exclusion wins. Sellers MUST reject a request they cannot enforce rather than silently dropping the exclusion.',
            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
    browser: Annotated[
        list[browser_family.BrowserFamily] | None,
        Field(
            description='Restrict delivery to specific canonical browser families in the impression delivery and rendering environment, not the post-click landing-page browser. Values MUST NOT be inferred solely from operating system, device, web/mobile-web inventory, or placement. Values in this array use OR semantics. When browser is supplied, families not listed are ineligible: other includes a seller-recognized family that is not explicitly enumerated, while unknown includes a browser the seller cannot classify into a recognized family. When the same family appears in browser and browser_exclude, exclusion wins. Browser and device constraints intersect; a seller that cannot enforce the exact combination MUST exclude or explicitly reconfigure the product during discovery and MUST reject it at create or update rather than silently widening delivery. Browser versions and seller-native IDs are intentionally unsupported.',
            min_length=1,
        ),
    ] = None
    browser_exclude: Annotated[
        list[browser_family.BrowserFamily] | None,
        Field(
            description='Exclude specific canonical browser families from delivery. other excludes seller-recognized families that are not explicitly enumerated; unknown excludes browsers the seller cannot classify into a recognized family. When the same family appears in browser and browser_exclude, exclusion wins. Sellers MUST reject a request they cannot enforce rather than silently dropping the exclusion.',
            min_length=1,
        ),
    ] = None
    store_catchments: Annotated[
        list[StoreCatchment] | None,
        Field(
            description='Target users within store catchment areas from a synced store catalog. Each entry references a store-type catalog and optionally narrows to specific stores or catchment zones.',
            min_length=1,
        ),
    ] = None
    geo_proximity: Annotated[
        list[GeoProximityItem] | 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
    language: Annotated[
        list[locale_tag.LanguageTag] | None,
        Field(
            description="Restrict to users with specific language preferences using canonical BCP 47 language ranges. Each buyer range is evaluated against a user's language-preference tag with RFC 4647 section 3.3.1 Basic Filtering: 'fr' matches 'fr', 'fr-CA', and 'fr-FR', while 'fr-CA' matches 'fr-CA' and more-specific descendants but not 'fr' or 'fr-FR'. Values use OR logic.",
            min_length=1,
            title='Targeting Languages',
        ),
    ] = 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,
            title='Targeting Keywords',
        ),
    ] = None
    negative_keywords: Annotated[
        list[negative_keyword.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,
            title='Targeting Negative Keywords',
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var age_restriction : AgeRestriction | None
var audience_exclude : list[str] | None
var audience_include : list[str] | None
var axe_exclude_segment : str | None
var axe_include_segment : str | None
var browser : list[BrowserFamily] | None
var browser_exclude : list[BrowserFamily] | None
var collection_list : CollectionListReference | None
var collection_list_exclude : CollectionListReference | None
var collection_selection : CollectionSelection1 | CollectionSelection2 | None
var daypart_targets : list[DaypartTarget] | None
var demographics : DemographicTargetingIntent | None
var device_platform : list[DevicePlatform] | None
var device_platform_exclude : list[DevicePlatform] | None
var device_type : list[DeviceType] | None
var device_type_exclude : list[DeviceType] | None
var frequency_cap : FrequencyCap | None
var geo_countries : list[GeoCountry] | None
var geo_countries_exclude : collections.abc.Sequence[GeoCountriesExcludeItem] | None
var geo_metros : list[GeoMetro] | None
var geo_metros_exclude : collections.abc.Sequence[GeoMetrosExcludeItem] | None
var geo_places : list[GeographicPlaceArea] | None
var geo_places_exclude : list[GeographicPlaceArea] | None
var geo_postal_areas : list[PostalArea] | None
var geo_postal_areas_exclude : collections.abc.Sequence[PostalArea] | None
var geo_proximity : list[GeoProximityItem] | None
var geo_regions : list[GeoRegion] | None
var geo_regions_exclude : collections.abc.Sequence[GeoRegionsExcludeItem] | None
var keyword_targets : list[KeywordTarget] | None
var language : list[LanguageTag] | None
var model_config
var negative_keywords : list[NegativeKeyword] | None
var placement_selection : PlacementSelection1 | PlacementSelection2 | None
var property_list : PropertyListReference | None
var property_list_exclude : PropertyListReference | None
var signal_targeting : list[SignalTargeting1 | SignalTargeting2 | SignalTargeting3] | None
var signal_targeting_groups : PackageSignalTargetingGroups | None
var store_catchments : list[StoreCatchment] | None

Inherited members

class TargetingOverlayInput (**data: Any)
Expand source code
class TargetingOverlayInput(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    geo_countries: Annotated[
        list[GeoCountry] | None,
        Field(
            description="Restrict delivery to specific countries. ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE').",
            min_length=1,
        ),
    ] = None
    geo_countries_exclude: Annotated[
        list[GeoCountriesExcludeItem] | None,
        Field(
            description="Exclude specific countries from delivery. ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE').",
            min_length=1,
        ),
    ] = None
    geo_regions: Annotated[
        list[GeoRegion] | None,
        Field(
            description='Restrict delivery to exact canonical ISO 3166-2 subdivisions (states, provinces, regions, departments, or other subdivision categories). Unknown identifiers are invalid. At create or update, sellers MUST reject unsupported identifiers and MUST NOT silently widen, drop, or partially apply the list. During get_products, a seller may instead return a sparse, buyer-reviewable targeting_resolution modification for a valid but unsupported requested outcome. Exact internal translation preserves accepted identifiers in package readback.',
            min_length=1,
        ),
    ] = None
    geo_regions_exclude: Annotated[
        list[GeoRegionsExcludeItem] | None,
        Field(
            description='Exclude exact canonical ISO 3166-2 subdivisions. Support is independent from geo_regions inclusion support. Unknown identifiers and values also present in geo_regions are invalid. At create or update, sellers MUST reject unsupported identifiers and partial application; during get_products, a seller may instead return a sparse, buyer-reviewable targeting_resolution modification for a valid but unsupported requested outcome.',
            min_length=1,
        ),
    ] = None
    geo_metros: Annotated[
        list[geo_metro.GeoMetro] | None,
        Field(
            description='Restrict delivery to specific metro areas. Each entry specifies the classification system and target values. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
            title='Targeting Geo Metros',
        ),
    ] = None
    geo_metros_exclude: Annotated[
        list[GeoMetrosExcludeItem] | None,
        Field(
            description='Exclude specific metro areas from delivery. Each entry specifies the classification system and excluded values. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_postal_areas: Annotated[
        list[postal_area.PostalArea] | None,
        Field(
            description='Restrict delivery to specific postal areas. Prefer the native country + postal system form. The deprecated legacy country-fused postal-system tokens remain accepted for compatibility. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_postal_areas_exclude: Annotated[
        list[postal_area.PostalArea] | None,
        Field(
            description='Exclude specific postal areas from delivery. Prefer the native country + postal system form. The deprecated legacy country-fused postal-system tokens remain accepted for compatibility. Seller must declare supported systems in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    geo_places: Annotated[
        list[geo_place_area.GeographicPlaceArea] | None,
        Field(
            description='Restrict delivery to catalog-backed named places. Values MUST be stable identifiers in the declared system, not display names. Sellers must declare supported systems, countries, and place types in get_adcp_capabilities and reject unsupported entries rather than silently dropping them.',
            min_length=1,
        ),
    ] = None
    geo_places_exclude: Annotated[
        list[geo_place_area.GeographicPlaceArea] | None,
        Field(
            description='Exclude catalog-backed named places. Uses the same identifier-based shape as geo_places. Sellers MUST reject overlap with geo_places for the same country, system, place_type, and value.',
            min_length=1,
        ),
    ] = None
    daypart_targets: Annotated[
        list[daypart_target.DaypartTarget] | None,
        Field(
            description='Restrict delivery to specific time windows. Each entry specifies days of week, an hour range, and an optional timezone that defaults to inventory_local. A concrete IANA zone uses one shared civil-time clock, while inventory_local evaluates each inventory unit in its seller-assigned local timezone. Entries are independent and MAY use different clocks.',
            min_length=1,
        ),
    ] = None
    axe_include_segment: Annotated[
        str | None,
        Field(
            deprecated=True,
            description='Deprecated: Use TMP provider fields instead. AXE segment ID to include for targeting.',
        ),
    ] = None
    axe_exclude_segment: Annotated[
        str | None,
        Field(
            deprecated=True,
            description='Deprecated: Use TMP provider fields instead. AXE segment ID to exclude from targeting.',
        ),
    ] = None
    audience_include: Annotated[
        list[str] | None,
        Field(
            description='Restrict delivery to members of these first-party CRM audiences. Only users present in the uploaded lists are eligible. References audience_id values from sync_audiences on the same seller account — audience IDs are not portable across sellers. Not for lookalike expansion — express that intent in the campaign brief. Seller must declare support in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    audience_exclude: Annotated[
        list[str] | None,
        Field(
            description='Suppress delivery to members of these first-party CRM audiences. Matched users are excluded regardless of other targeting. References audience_id values from sync_audiences on the same seller account — audience IDs are not portable across sellers. Seller must declare support in get_adcp_capabilities.',
            min_length=1,
        ),
    ] = None
    signal_targeting_groups: package_signal_targeting_groups.PackageSignalTargetingGroups | None = (
        None
    )
    signal_targeting: Annotated[
        list[signal_targeting_1.SignalTargeting] | None,
        Field(
            deprecated=True,
            description='DEPRECATED. Use signal_targeting_groups for package-level signal targeting. Legacy flat signal_targeting remains accepted during the SignalRef migration window but cannot express grouped include/exclude composition or product-scoped pricing.',
            min_length=1,
        ),
    ] = None
    demographics: demographic_targeting_intent.DemographicTargetingIntent | None = None
    frequency_cap: frequency_cap_1.FrequencyCap | None = None
    property_list: property_list_ref.PropertyListReference | None = None
    property_list_exclude: property_list_ref.PropertyListReference | None = None
    collection_list: collection_list_ref.CollectionListReference | None = None
    collection_list_exclude: collection_list_ref.CollectionListReference | None = None
    placement_selection: placement_selection_1.PlacementSelection | None = None
    collection_selection: collection_selection_1.CollectionSelection | None = None
    age_restriction: targeting.AgeRestriction | None = None
    device_platform: Annotated[
        list[device_platform_1.DevicePlatform] | None,
        Field(
            description='Restrict to specific platforms. Use for technical compatibility (app only works on iOS). Values from Sec-CH-UA-Platform standard, extended for CTV.',
            min_length=1,
        ),
    ] = None
    device_platform_exclude: Annotated[
        list[device_platform_1.DevicePlatform] | None,
        Field(
            description='Exclude specific operating-system platforms from delivery. When a platform appears in both device_platform and device_platform_exclude, exclusion wins. Sellers MUST reject a request they cannot enforce rather than silently dropping the exclusion.',
            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
    browser: Annotated[
        list[browser_family.BrowserFamily] | None,
        Field(
            description='Restrict delivery to specific canonical browser families in the impression delivery and rendering environment, not the post-click landing-page browser. Values MUST NOT be inferred solely from operating system, device, web/mobile-web inventory, or placement. Values in this array use OR semantics. When browser is supplied, families not listed are ineligible: other includes a seller-recognized family that is not explicitly enumerated, while unknown includes a browser the seller cannot classify into a recognized family. When the same family appears in browser and browser_exclude, exclusion wins. Browser and device constraints intersect; a seller that cannot enforce the exact combination MUST exclude or explicitly reconfigure the product during discovery and MUST reject it at create or update rather than silently widening delivery. Browser versions and seller-native IDs are intentionally unsupported.',
            min_length=1,
        ),
    ] = None
    browser_exclude: Annotated[
        list[browser_family.BrowserFamily] | None,
        Field(
            description='Exclude specific canonical browser families from delivery. other excludes seller-recognized families that are not explicitly enumerated; unknown excludes browsers the seller cannot classify into a recognized family. When the same family appears in browser and browser_exclude, exclusion wins. Sellers MUST reject a request they cannot enforce rather than silently dropping the exclusion.',
            min_length=1,
        ),
    ] = None
    store_catchments: Annotated[
        list[StoreCatchment] | None,
        Field(
            description='Target users within store catchment areas from a synced store catalog. Each entry references a store-type catalog and optionally narrows to specific stores or catchment zones.',
            min_length=1,
        ),
    ] = None
    geo_proximity: Annotated[
        list[GeoProximityItem] | 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
    language: Annotated[
        list[locale_tag.LanguageTag] | None,
        Field(
            description="Restrict to users with specific language preferences using canonical BCP 47 language ranges. Each buyer range is evaluated against a user's language-preference tag with RFC 4647 section 3.3.1 Basic Filtering: 'fr' matches 'fr', 'fr-CA', and 'fr-FR', while 'fr-CA' matches 'fr-CA' and more-specific descendants but not 'fr' or 'fr-FR'. Values use OR logic.",
            min_length=1,
            title='Targeting Languages',
        ),
    ] = 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,
            title='Targeting Keywords',
        ),
    ] = None
    negative_keywords: Annotated[
        list[negative_keyword.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,
            title='Targeting Negative Keywords',
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var age_restriction : AgeRestriction | None
var audience_exclude : list[str] | None
var audience_include : list[str] | None
var axe_exclude_segment : str | None
var axe_include_segment : str | None
var browser : list[BrowserFamily] | None
var browser_exclude : list[BrowserFamily] | None
var collection_list : CollectionListReference | None
var collection_list_exclude : CollectionListReference | None
var collection_selection : CollectionSelection1 | CollectionSelection2 | None
var daypart_targets : list[DaypartTarget] | None
var demographics : DemographicTargetingIntent | None
var device_platform : list[DevicePlatform] | None
var device_platform_exclude : list[DevicePlatform] | None
var device_type : list[DeviceType] | None
var device_type_exclude : list[DeviceType] | None
var frequency_cap : FrequencyCap | None
var geo_countries : list[GeoCountry] | None
var geo_countries_exclude : list[GeoCountriesExcludeItem] | None
var geo_metros : list[GeoMetro] | None
var geo_metros_exclude : list[GeoMetrosExcludeItem] | None
var geo_places : list[GeographicPlaceArea] | None
var geo_places_exclude : list[GeographicPlaceArea] | None
var geo_postal_areas : list[PostalArea] | None
var geo_postal_areas_exclude : list[PostalArea] | None
var geo_proximity : list[GeoProximityItem] | None
var geo_regions : list[GeoRegion] | None
var geo_regions_exclude : list[GeoRegionsExcludeItem] | None
var keyword_targets : list[KeywordTarget] | None
var language : list[LanguageTag] | None
var model_config
var negative_keywords : list[NegativeKeyword] | None
var placement_selection : PlacementSelection1 | PlacementSelection2 | None
var property_list : PropertyListReference | None
var property_list_exclude : PropertyListReference | None
var signal_targeting : list[SignalTargeting1 | SignalTargeting2 | SignalTargeting3] | None
var signal_targeting_groups : PackageSignalTargetingGroups | None
var store_catchments : list[StoreCatchment] | None

Inherited members

class Totals (**data: Any)
Expand source code
class Totals(DeliveryMetrics):
    effective_rate: Annotated[
        StrictFloat | None,
        Field(
            description="Effective rate paid per unit based on pricing_model (e.g., actual CPM for 'cpm', actual cost per completed view for 'cpcv', actual cost per point for 'cpp')",
            ge=0.0,
        ),
    ] = None

Base model for AdCP types with spec-compliant serialization.

Defaults to extra='ignore' so unknown fields from newer spec versions are silently dropped rather than causing validation errors. Generated types whose schemas set additionalProperties: true override this with extra='allow' in their own model_config.

Set ADCP_STRICT_VALIDATION=1 in the environment ("1", "true", "yes", "on" are accepted) to flip the default to extra='forbid'. Use this during spec upgrades to catch silently-dropped renamed fields in tests. See :func:_resolve_extra_policy.

Important

The env var is resolved once at module import time. Set it in your shell or CI environment before import adcp runs — mutating os.environ["ADCP_STRICT_VALIDATION"] after the first adcp import has no effect on already-imported model classes (they captured the policy at class-body evaluation).

Consumers who want per-model strict validation can override model_config on their subclass.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var effective_rate : float | None
var model_config

Inherited members

class UpdateMediaBuyRequest (**data: Any)
Expand source code
class UpdateMediaBuyRequest(_LegacyUpdateMediaBuyRequest, CanonicalBoundaryModel):
    """Canonical update request; both package lists are canonical."""

    packages: list[PackageUpdate] | None = None
    new_packages: list[PackageRequest] | None = Field(  # type: ignore[assignment]
        default=None, min_length=1
    )

Canonical update request; both package lists are canonical.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var new_packages : list[PackageRequest] | None
var packages : list[PackageUpdate] | None

Instance variables

var adcp_major_version : int | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
class UpdateMediaBuyPackagesRequest (**data: Any)
Expand source code
class UpdateMediaBuyRequest(_LegacyUpdateMediaBuyRequest, CanonicalBoundaryModel):
    """Canonical update request; both package lists are canonical."""

    packages: list[PackageUpdate] | None = None
    new_packages: list[PackageRequest] | None = Field(  # type: ignore[assignment]
        default=None, min_length=1
    )

Canonical update request; both package lists are canonical.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var new_packages : list[PackageRequest] | None
var packages : list[PackageUpdate] | None

Instance variables

var adcp_major_version : int | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
class UpdateMediaBuyPropertiesRequest (**data: Any)
Expand source code
class UpdateMediaBuyRequest(_LegacyUpdateMediaBuyRequest, CanonicalBoundaryModel):
    """Canonical update request; both package lists are canonical."""

    packages: list[PackageUpdate] | None = None
    new_packages: list[PackageRequest] | None = Field(  # type: ignore[assignment]
        default=None, min_length=1
    )

Canonical update request; both package lists are canonical.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var new_packages : list[PackageRequest] | None
var packages : list[PackageUpdate] | None

Inherited members

class UpdateMediaBuySuccessResponse (**data: Any)
Expand source code
class UpdateMediaBuyResponse1(_LegacyUpdateMediaBuyResponse1, CanonicalBoundaryModel):
    """Canonical update response preserving the 3.x legacy-status normalizer."""

    affected_packages: Sequence[Package] | None = None

    @model_validator(mode="before")
    @classmethod
    def _normalize_legacy_status(cls, data: Any) -> Any:
        if not isinstance(data, dict):
            return data
        raw_status = unwrap_enum_value(data.get("status"))
        media_buy_status = unwrap_enum_value(data.get("media_buy_status"))
        if raw_status is None or raw_status == "completed":
            return {**data, "status": "completed"}
        if media_buy_status is None and raw_status in MEDIA_BUY_LEGACY_STATUS_VALUES:
            return {**data, "media_buy_status": raw_status, "status": "completed"}
        if media_buy_status is not None and raw_status == media_buy_status:
            return {**data, "status": "completed"}
        return data

Canonical update response preserving the 3.x legacy-status normalizer.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var affected_packages : collections.abc.Sequence[Package] | None
var model_config

Inherited members

class UpdateMediaBuyErrorResponse (**data: Any)
Expand source code
class UpdateMediaBuyResponse2(_LegacyUpdateMediaBuyResponse2, CanonicalBoundaryModel):
    """Canonical update-media-buy error arm."""

Canonical update-media-buy error arm.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members

class UpdateMediaBuySubmittedResponse (**data: Any)
Expand source code
class UpdateMediaBuyResponse3(_LegacyUpdateMediaBuyResponse3, CanonicalBoundaryModel):
    """Canonical update-media-buy submitted arm."""

Canonical update-media-buy submitted arm.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members