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 = NoneBase 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 setadditionalProperties: trueoverride this withextra='allow'in their ownmodel_config.Set
ADCP_STRICT_VALIDATION=1in the environment ("1","true","yes","on"are accepted) to flip the default toextra='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 adcpruns — mutatingos.environ["ADCP_STRICT_VALIDATION"]after the firstadcpimport 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_configon 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- AdCPBaseModel
- pydantic.main.BaseModel
Class variables
var account_id : strvar creative_id : strvar ext : ExtensionObject | Nonevar fired_at : pydantic.types.AwareDatetimevar idempotency_key : strvar initiator : Initiatorvar model_configvar notification_id : strvar notification_type : Literal['creative.status_changed']var reason_code : CreativeEventReasonCodevar reason_detail : str | Nonevar revision_id : CreativeRevisionId | Nonevar subscriber_id : strvar 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 approvedvar pending_reviewvar processingvar 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 sellervar 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 setadditionalProperties: trueoverride this withextra='allow'in their ownmodel_config.Set
ADCP_STRICT_VALIDATION=1in the environment ("1","true","yes","on"are accepted) to flip the default toextra='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 adcpruns — mutatingos.environ["ADCP_STRICT_VALIDATION"]after the firstadcpimport 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_configon 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- AdCPBaseModel
- pydantic.main.BaseModel
Class variables
var from_ : Fromvar model_configvar observed_at : pydantic.types.AwareDatetimevar to : CreativeStatus
Inherited members