Module adcp.types.domains.account.report_usage_request

Classes

class ReportUsageRequest (**data: Any)
Expand source code
class ReportUsageRequest(AdcpRequest, AdcpVersionEnvelope):
    model_config = ConfigDict(
        extra='allow',
    )
    idempotency_key: Annotated[
        str,
        Field(
            description='Client-generated unique key for this request. If a request with the same key has already been accepted, the server returns the original response without re-processing. MUST be unique per (seller, request) pair to prevent cross-seller correlation. Use a fresh UUID v4 for each request. Prevents duplicate billing on retries.'
        ),
    ]
    reporting_period: Annotated[
        datetime_range.DatetimeRange,
        Field(
            description='The time range covered by this usage report. Applies to all records in the request.'
        ),
    ]
    usage: Annotated[
        list[UsageItem],
        Field(
            description='One or more usage records. Each record is self-contained: it carries its own account, allowing a single request to span multiple accounts.',
            min_length=1,
        ),
    ]
    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 context : ContextObject | None
var ext : ExtensionObject | None
var idempotency_key : str
var model_config
var reporting_period : DatetimeRange
var usage : list[UsageItem]

Inherited members

class UsageItem (**data: Any)
Expand source code
class UsageItem(AdCPBaseModel):
    model_config = ConfigDict(
        extra='allow',
    )
    account: Annotated[
        account_ref.AccountReference, Field(description='Account for this usage record.')
    ]
    media_buy_id: Annotated[
        str | None,
        Field(
            description='Seller-assigned media buy identifier. Links this usage record to a specific media buy.'
        ),
    ] = None
    vendor_cost: Annotated[
        StrictFloat,
        Field(
            description='Amount owed to the vendor for this record, denominated in currency.',
            ge=0.0,
        ),
    ]
    currency: Annotated[str, Field(description='ISO 4217 currency code.', pattern='^[A-Z]{3}$')]
    pricing_option_id: Annotated[
        str | None,
        Field(
            description="Pricing option identifier from the vendor's discovery response (e.g., get_signals, list_content_standards). The vendor uses this to verify the correct rate was applied."
        ),
    ] = None
    impressions: Annotated[
        SchemaInt | None,
        Field(description='Impressions delivered using this vendor service.', ge=0),
    ] = None
    media_spend: Annotated[
        StrictFloat | None,
        Field(
            description='Media spend in currency for the period. Required when a percent_of_media pricing model was used, so the vendor can verify the applied rate.',
            ge=0.0,
        ),
    ] = None
    conversions: Annotated[
        StrictFloat | None,
        Field(
            description='Number of attributed conversion events for the reporting period. Optional analytics context for revenue_share reconciliation.',
            ge=0.0,
        ),
    ] = None
    conversion_value: Annotated[
        StrictFloat | None,
        Field(
            description='Total monetary value of attributed conversions for the reporting period, in currency. Optional analytics context for revenue_share reconciliation; it is not the billing basis.',
            ge=0.0,
        ),
    ] = None
    commissionable_value: Annotated[
        StrictFloat | None,
        Field(
            description='Settled attributed value eligible for commission, in currency. Required when pricing_option_id selects a revenue_share option. The receiver verifies vendor_cost = round_currency(commissionable_value × the selected commission_rate).',
            ge=0.0,
        ),
    ] = None
    signal_agent_segment_id: Annotated[
        str | None,
        Field(description='Signal identifier from get_signals. Required for signals agents.'),
    ] = None
    standards_id: Annotated[
        str | None,
        Field(
            description='Content standards configuration identifier. Required for governance agents.'
        ),
    ] = None
    rights_id: Annotated[
        str | None,
        Field(
            description='Rights grant identifier from acquire_rights. Required for brand/rights agents. Links usage records to specific rights grants for cap tracking, billing verification, and overage calculation.'
        ),
    ] = None
    creative_id: Annotated[
        str | None,
        Field(
            description='Creative identifier from build_creative or list_creatives. Required for creative agents. Links usage records to specific creatives for billing verification.'
        ),
    ] = None
    build_variant_id: Annotated[
        str | None,
        Field(
            description='Optional. When the reported creative_id was promoted from a specific build_creative variant leaf but the creative_id differs from the source build_variant_id, carry that source build_variant_id so billing reconciliation can link this usage record back to the exact produced leaf for audit (pricing_option_id alone is not unique across leaves). On the canonical path where creative_id is the build_variant_id, omit this field and use creative_id as the join key. Omit for creatives with no build-variant lineage.'
        ),
    ] = None
    property_list_id: Annotated[
        str | None,
        Field(
            description='Property list identifier from list_property_lists. Required for property list agents. Links usage records to specific property lists for billing verification.'
        ),
    ] = None
    final: Annotated[
        StrictBool | None,
        Field(
            description="Whether this usage record represents the reporter's final, billing-authoritative numbers for the reporting period. **Absent means unknown** — the reporter has not declared finality on this record. Set `true` only when the reporter has actually settled the numbers (e.g., 3PAS month-end close after SIVT scrubbing, conversion dedup, and view-through windows have closed; vendor file post-C7 for broadcast). Set `false` when pushing preliminary measurements (daily pacing pushes, intra-period progress) that are still settling. Receivers MUST NOT invoice on `final: false` records, and MUST NOT invoice on records where `final` is absent for buys whose `measurement_terms.billing_measurement` names this reporter as authoritative — request a final record first. Receivers MAY invoice on absent for buys with no `measurement_terms.billing_measurement` (3.0-style usage where the receiver treats reports as authoritative on receipt) and for non-media-buy variants (signals, governance, creative, brand — domains with no provisional state concept). When the same `(account, media_buy_id, reporting_period)` is later reported with `final: true`, that record supersedes any prior records for the period."
        ),
    ] = None
    finalized_at: Annotated[
        AwareDatetime | None,
        Field(
            description="ISO 8601 timestamp at which the reporter considered these numbers final. Present only when `final: true`. Anchors any deadline declared in the buy's `measurement_terms.billing_measurement.finalization_deadline_hours`."
        ),
    ] = None
    measurement_window: Annotated[
        str | None,
        Field(
            description="Which measurement window this record represents, referencing a window_id from the product's reporting_capabilities.measurement_windows or from `measurement_terms.billing_measurement.measurement_window`. Examples: 'c7' for broadcast TV, 'post_sivt' for digital post-IVT, 'downloads_30d' for podcast. When absent, the record is not windowed (standard digital reporting). When the buy's `measurement_terms.billing_measurement.measurement_window` is set, reporters SHOULD include `measurement_window` so the receiver can reconcile against the correct stage.",
            examples=['live', 'c3', 'c7', 'post_ivt', 'post_sivt', 'downloads_30d'],
            max_length=50,
        ),
    ] = 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 account : AccountReference1 | AccountReference2
var build_variant_id : str | None
var commissionable_value : float | None
var conversion_value : float | None
var conversions : float | None
var creative_id : str | None
var currency : str
var final : bool | None
var finalized_at : pydantic.types.AwareDatetime | None
var impressions : int | None
var measurement_window : str | None
var media_buy_id : str | None
var media_spend : float | None
var model_config
var pricing_option_id : str | None
var property_list_id : str | None
var rights_id : str | None
var signal_agent_segment_id : str | None
var standards_id : str | None
var vendor_cost : float

Inherited members