Module adcp.types.domains.creative.creative_status_changed_webhook

Classes

class CreativeStatusChangedWebhook (**data: Any)
Expand source code
class CreativeStatusChangedWebhook(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    idempotency_key: Annotated[
        str,
        Field(
            description='Sender-generated key stable across retries of the same fire. Sellers MUST generate a cryptographically random value (UUID v4 recommended) per distinct fire and reuse it on every retry of the same fire. Receivers MUST dedupe by this key, scoped to the authenticated sender identity. Distinct from `notification_id` — same `notification_id` under two different `idempotency_key` values is a re-emission signal (snapshot-and-log Rule 1).',
            max_length=255,
            min_length=16,
            pattern='^[A-Za-z0-9_.:-]{16,255}$',
        ),
    ]
    notification_id: Annotated[
        str,
        Field(
            description="Stable identifier for this logical transition event, used by buyers to correlate fires to current snapshot state. Stable across re-emissions of the same transition (e.g., the seller re-fires after the buyer's endpoint was unreachable); a fresh transition on the same creative receives a new id. Charset matches `idempotency_key` so the value is safe to log, embed in dashboard URLs, and pass into LLM prompts without escaping.",
            max_length=255,
            min_length=1,
            pattern='^[A-Za-z0-9_.:-]{1,255}$',
        ),
    ]
    notification_type: Annotated[
        Literal['creative.status_changed'],
        Field(
            description="Fixed notification type discriminator. Matches the value registered on the subscriber's `event_types`."
        ),
    ] = 'creative.status_changed'
    fired_at: Annotated[
        AwareDatetime,
        Field(
            description="ISO 8601 timestamp when the seller initiated this fire. Distinct from when the transition was observed (`transition.observed_at`) — fires MAY be coalesced or delayed up to the seller's declared coalescence window for `creative.status_changed`."
        ),
    ]
    subscriber_id: Annotated[
        str,
        Field(
            description="Identifies which `notification_configs[]` entry on the recipient account is receiving this fire. Echoed verbatim from the entry's `subscriber_id`. Required so multi-subscriber accounts can route by endpoint.",
            max_length=64,
            min_length=1,
            pattern='^[A-Za-z0-9_.:-]{1,64}$',
        ),
    ]
    account_id: Annotated[
        str,
        Field(
            description="Seller's identifier for the account this creative belongs to. Echoed so multi-account buyers can route without re-resolving via `list_accounts`."
        ),
    ]
    creative_id: Annotated[
        str,
        Field(
            description="Seller's identifier for the creative whose status changed. References the same id space as `list_creatives` / `sync_creatives` responses."
        ),
    ]
    revision_id: Annotated[
        creative_revision_id.CreativeRevisionId | None,
        Field(
            description='Buyer-authored input revision to which this transition applies. Required when the reviewed state has revision identity and the seller advertises creative.supports_revisions; it need not be the current list revision when the fire is received. Omitted for unversioned current content. A stale review outcome MUST NOT emit a fire as though it applied to a newer current revision.'
        ),
    ] = None
    transition: Annotated[
        Transition,
        Field(
            description='The status transition that triggered this fire. Valid `from` values are restricted to the prior states from which a seller/system-initiated transition can fire (`processing` for processing outcomes, `pending_review` for initial review outcomes, `approved` for re-review/revocation/seller-archive/recoverable suspension, `suspended` for seller-observed recovery or terminal escalation). The post-terminal states `rejected` and `archived` MUST NOT appear as `from` — those would require a buyer-initiated unblock (`sync_creatives` resubmit / unarchive), which does not fire this event.'
        ),
    ]
    reason_code: Annotated[
        creative_event_reason_code.CreativeEventReasonCode,
        Field(
            description='Categorical reason for the transition. Per-transition valid subsets are constrained by impairment.coherence-style narrative rules — see docs/creative/creative-lifecycle-webhooks.mdx. Receivers MUST treat unknown reason codes as forward-compatible additions and not reject the fire.'
        ),
    ]
    reason_detail: Annotated[
        str | None,
        Field(
            description='Human-readable supplement to `reason_code`. Free text from the seller. Sellers MUST NOT include third-party PII in this field.',
            max_length=500,
        ),
    ] = None
    initiator: Annotated[
        Initiator,
        Field(
            description='Who initiated the transition. `seller` — explicit decision by the seller (human reviewer, policy operator, takedown handler). `system` — automated seller-side process (processing pipeline, retention sweep, inactivity scan). `buyer` never appears on this event — buyer-initiated transitions are acknowledged on the `sync_creatives` response path and MUST NOT fire `creative.status_changed`.'
        ),
    ]
    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 account_id : str
var creative_id : str
var ext : ExtensionObject | None
var fired_at : pydantic.types.AwareDatetime
var idempotency_key : str
var initiator : Initiator
var model_config
var notification_id : str
var notification_type : Literal['creative.status_changed']
var reason_code : CreativeEventReasonCode
var reason_detail : str | None
var revision_id : CreativeRevisionId | None
var subscriber_id : str
var transition : Transition

Inherited members

class From (*args, **kwds)
Expand source code
class From(StrEnum):
    processing = 'processing'
    pending_review = 'pending_review'
    approved = 'approved'
    suspended = 'suspended'

Enum where members are also (and must be) strings

Ancestors

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

Class variables

var approved
var pending_review
var processing
var suspended
class Initiator (*args, **kwds)
Expand source code
class Initiator(StrEnum):
    seller = 'seller'
    system = 'system'

Enum where members are also (and must be) strings

Ancestors

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

Class variables

var seller
var system
class Transition (**data: Any)
Expand source code
class Transition(AdCPBaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    from_: Annotated[
        From,
        Field(
            alias='from',
            description='Prior status — restricted to the states from which a seller/system-initiated transition can fire. For initial review outcomes the prior status is `pending_review`; for processing outcomes it is `processing`; for seller re-review, post-approval revocation, recoverable suspension, and seller-initiated archive it is `approved`; for recovery from a dependency/authorization outage or terminal escalation after a suspension it is `suspended`.',
        ),
    ]
    to: Annotated[creative_status.CreativeStatus, Field(description='New status.')]
    observed_at: Annotated[
        AwareDatetime,
        Field(
            description="ISO 8601 timestamp when the seller observed the transition. Distinct from `fired_at` — `fired_at` reflects when the webhook was emitted, which may lag `observed_at` by up to the seller's coalescence window. **Ordering with `media-buy.impairment`:** when a creative transition also causes a media-buy impairment (e.g., `approved → suspended` or `approved → rejected` while assignments exist), the `creative.status_changed` and `impairment` fires are not ordered — buyers MUST NOT assume one arrives before the other. Reconcile via the snapshot (`list_creatives` and `get_media_buys`) when the two fires reference the same `creative_id`."
        ),
    ]

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 from_ : From
var model_config
var observed_at : pydantic.types.AwareDatetime
var to : CreativeStatus

Inherited members