Package adcp
Sub-modules
adcp.adagentsadcp.clientadcp.configadcp.exceptionsadcp.protocolsadcp.registryadcp.server-
ADCP Server Framework …
adcp.simple-
Simplified API accessor for ADCPClient …
adcp.testing-
Test helpers for AdCP client library …
adcp.types-
AdCP Type System …
adcp.utilsadcp.validation-
Runtime validation for AdCP data structures …
adcp.webhooks-
Webhook creation and signing utilities for AdCP agents.
Functions
def create_a2a_webhook_payload(task_id: str,
status: TaskStatus,
context_id: str,
result: AdcpAsyncResponseData | dict[str, Any],
timestamp: datetime | None = None) ‑> a2a.types.Task | a2a.types.TaskStatusUpdateEvent-
Expand source code
def create_a2a_webhook_payload( task_id: str, status: GeneratedTaskStatus, context_id: str, result: AdcpAsyncResponseData | dict[str, Any], timestamp: datetime | None = None, ) -> Task | TaskStatusUpdateEvent: """ Create A2A webhook payload (Task or TaskStatusUpdateEvent). Per A2A specification: - Terminated statuses (completed, failed): Returns Task with artifacts[].parts[] - Intermediate statuses (working, input-required, submitted): Returns TaskStatusUpdateEvent with status.message.parts[] This function helps agent implementations construct properly formatted A2A webhook payloads for sending to clients. Args: task_id: Unique identifier for the task status: Current task status context_id: Session/conversation identifier (required by A2A protocol) timestamp: When the webhook was generated (defaults to current UTC time) result: Task-specific payload (AdCP response data) Returns: Task object for terminated statuses, TaskStatusUpdateEvent for intermediate statuses Examples: Create a completed Task webhook: >>> from adcp.webhooks import create_a2a_webhook_payload >>> from adcp.types import GeneratedTaskStatus >>> >>> task = create_a2a_webhook_payload( ... task_id="task_123", ... status=GeneratedTaskStatus.completed, ... result={"products": [...]}, ... message="Found 5 products" ... ) >>> # task is a Task object with artifacts containing the result Create a working status update: >>> event = create_a2a_webhook_payload( ... task_id="task_456", ... status=GeneratedTaskStatus.working, ... message="Processing 3 of 10 items" ... ) >>> # event is a TaskStatusUpdateEvent with status.message Send A2A webhook via HTTP POST: >>> import httpx >>> from a2a.types import Task >>> >>> payload = create_a2a_webhook_payload(...) >>> # Serialize to dict for JSON >>> if isinstance(payload, Task): ... payload_dict = payload.model_dump(mode='json') ... else: ... payload_dict = payload.model_dump(mode='json') >>> >>> response = await httpx.post(webhook_url, json=payload_dict) """ if timestamp is None: timestamp = datetime.now(timezone.utc) # Convert datetime to ISO string for A2A protocol timestamp_str = timestamp.isoformat() if isinstance(timestamp, datetime) else timestamp # Map GeneratedTaskStatus to A2A status state string status_value = status.value if hasattr(status, "value") else str(status) # Map AdCP status to A2A status state # Note: A2A uses "input-required" (hyphenated) while AdCP uses "input_required" (underscore) status_mapping = { "completed": "completed", "failed": "failed", "working": "working", "submitted": "submitted", "input_required": "input-required", } a2a_status_state = status_mapping.get(status_value, status_value) # Build parts for the message/artifact parts: list[Part] = [] # Add DataPart # Convert AdcpAsyncResponseData to dict if it's a Pydantic model if hasattr(result, "model_dump"): result_dict: dict[str, Any] = result.model_dump(mode="json") else: result_dict = result data_part = DataPart(data=result_dict) parts.append(Part(root=data_part)) # Determine if this is a terminated status (Task) or intermediate (TaskStatusUpdateEvent) is_terminated = status in [GeneratedTaskStatus.completed, GeneratedTaskStatus.failed] # Convert string to TaskState enum task_state_enum = TaskState(a2a_status_state) if is_terminated: # Create Task object with artifacts for terminated statuses task_status = TaskStatus(state=task_state_enum, timestamp=timestamp_str) # Build artifact with parts # Note: Artifact requires artifact_id, use task_id as prefix if parts: artifact = Artifact( artifact_id=f"{task_id}_result", parts=parts, ) artifacts = [artifact] else: artifacts = [] return Task( id=task_id, status=task_status, artifacts=artifacts, context_id=context_id, ) else: # Create TaskStatusUpdateEvent with status.message for intermediate statuses # Build message with parts if parts: message_obj = Message( message_id=f"{task_id}_msg", role=Role.agent, # Agent is responding parts=parts, ) else: message_obj = None task_status = TaskStatus( state=task_state_enum, timestamp=timestamp_str, message=message_obj ) return TaskStatusUpdateEvent( task_id=task_id, status=task_status, context_id=context_id, final=False, # Intermediate statuses are not final )Create A2A webhook payload (Task or TaskStatusUpdateEvent).
Per A2A specification: - Terminated statuses (completed, failed): Returns Task with artifacts[].parts[] - Intermediate statuses (working, input-required, submitted): Returns TaskStatusUpdateEvent with status.message.parts[]
This function helps agent implementations construct properly formatted A2A webhook payloads for sending to clients.
Args
task_id- Unique identifier for the task
status- Current task status
context_id- Session/conversation identifier (required by A2A protocol)
timestamp- When the webhook was generated (defaults to current UTC time)
result- Task-specific payload (AdCP response data)
Returns
Task object for terminated statuses, TaskStatusUpdateEvent for intermediate statuses
Examples
Create a completed Task webhook:
>>> from adcp.webhooks import create_a2a_webhook_payload >>> from adcp.types import GeneratedTaskStatus >>> >>> task = create_a2a_webhook_payload( ... task_id="task_123", ... status=GeneratedTaskStatus.completed, ... result={"products": [...]}, ... message="Found 5 products" ... ) >>> # task is a Task object with artifacts containing the resultCreate a working status update:
>>> event = create_a2a_webhook_payload( ... task_id="task_456", ... status=GeneratedTaskStatus.working, ... message="Processing 3 of 10 items" ... ) >>> # event is a TaskStatusUpdateEvent with status.messageSend A2A webhook via HTTP POST:
>>> import httpx >>> from a2a.types import Task >>> >>> payload = create_a2a_webhook_payload(...) >>> # Serialize to dict for JSON >>> if isinstance(payload, Task): ... payload_dict = payload.model_dump(mode='json') ... else: ... payload_dict = payload.model_dump(mode='json') >>> >>> response = await httpx.post(webhook_url, json=payload_dict) def create_mcp_webhook_payload(task_id: str,
status: TaskStatus,
result: AdcpAsyncResponseData | dict[str, Any] | None = None,
timestamp: datetime | None = None,
task_type: str | None = None,
operation_id: str | None = None,
message: str | None = None,
context_id: str | None = None,
domain: str | None = None) ‑> dict[str, typing.Any]-
Expand source code
def create_mcp_webhook_payload( task_id: str, status: GeneratedTaskStatus, result: AdcpAsyncResponseData | dict[str, Any] | None = None, timestamp: datetime | None = None, task_type: str | None = None, operation_id: str | None = None, message: str | None = None, context_id: str | None = None, domain: str | None = None, ) -> dict[str, Any]: """ Create MCP webhook payload dictionary. This function helps agent implementations construct properly formatted webhook payloads for sending to clients. Args: task_id: Unique identifier for the task status: Current task status task_type: Optionally type of AdCP operation (e.g., "get_products", "create_media_buy") timestamp: When the webhook was generated (defaults to current UTC time) result: Task-specific payload (AdCP response data) operation_id: Publisher-defined operation identifier (deprecated from payload, should be in URL routing, but included for backward compatibility) message: Human-readable summary of task state context_id: Session/conversation identifier domain: AdCP domain this task belongs to Returns: Dictionary matching McpWebhookPayload schema, ready to be sent as JSON Examples: Create a completed webhook with results: >>> from adcp.webhooks import create_mcp_webhook_payload >>> from adcp.types import GeneratedTaskStatus >>> >>> payload = create_mcp_webhook_payload( ... task_id="task_123", ... task_type="get_products", ... status=GeneratedTaskStatus.completed, ... result={"products": [...]}, ... message="Found 5 products" ... ) Create a failed webhook with error: >>> payload = create_mcp_webhook_payload( ... task_id="task_456", ... task_type="create_media_buy", ... status=GeneratedTaskStatus.failed, ... result={"errors": [{"code": "INVALID_INPUT", "message": "..."}]}, ... message="Validation failed" ... ) Create a working status update: >>> payload = create_mcp_webhook_payload( ... task_id="task_789", ... task_type="sync_creatives", ... status=GeneratedTaskStatus.working, ... message="Processing 3 of 10 creatives" ... ) """ if timestamp is None: timestamp = datetime.now(timezone.utc) # Convert status enum to string value status_value = status.value if hasattr(status, "value") else str(status) # Build payload matching McpWebhookPayload schema payload: dict[str, Any] = { "task_id": task_id, "task_type": task_type, "status": status_value, "timestamp": timestamp.isoformat() if isinstance(timestamp, datetime) else timestamp, } # Add optional fields only if provided if result is not None: # Convert Pydantic model to dict if needed for JSON serialization if hasattr(result, "model_dump"): payload["result"] = result.model_dump(mode="json") else: payload["result"] = result if operation_id is not None: payload["operation_id"] = operation_id if message is not None: payload["message"] = message if context_id is not None: payload["context_id"] = context_id if domain is not None: payload["domain"] = domain return payloadCreate MCP webhook payload dictionary.
This function helps agent implementations construct properly formatted webhook payloads for sending to clients.
Args
task_id- Unique identifier for the task
status- Current task status
task_type- Optionally type of AdCP operation (e.g., "get_products", "create_media_buy")
timestamp- When the webhook was generated (defaults to current UTC time)
result- Task-specific payload (AdCP response data)
operation_id- Publisher-defined operation identifier (deprecated from payload, should be in URL routing, but included for backward compatibility)
message- Human-readable summary of task state
context_id- Session/conversation identifier
domain- AdCP domain this task belongs to
Returns
Dictionary matching McpWebhookPayload schema, ready to be sent as JSON
Examples
Create a completed webhook with results:
>>> from adcp.webhooks import create_mcp_webhook_payload >>> from adcp.types import GeneratedTaskStatus >>> >>> payload = create_mcp_webhook_payload( ... task_id="task_123", ... task_type="get_products", ... status=GeneratedTaskStatus.completed, ... result={"products": [...]}, ... message="Found 5 products" ... )Create a failed webhook with error:
>>> payload = create_mcp_webhook_payload( ... task_id="task_456", ... task_type="create_media_buy", ... status=GeneratedTaskStatus.failed, ... result={"errors": [{"code": "INVALID_INPUT", "message": "..."}]}, ... message="Validation failed" ... )Create a working status update:
>>> payload = create_mcp_webhook_payload( ... task_id="task_789", ... task_type="sync_creatives", ... status=GeneratedTaskStatus.working, ... message="Processing 3 of 10 creatives" ... ) def create_test_agent(**overrides: Any) ‑> AgentConfig-
Expand source code
def create_test_agent(**overrides: Any) -> AgentConfig: """Create a custom test agent configuration. Useful when you need to modify the default test agent setup. Args: **overrides: Keyword arguments to override default config values Returns: Complete agent configuration Example: ```python from adcp.testing import create_test_agent from adcp.client import ADCPClient # Use default test agent with custom ID config = create_test_agent(id="my-test-agent") client = ADCPClient(config) ``` Example: ```python # Use A2A protocol instead of MCP from adcp.types.core import Protocol config = create_test_agent( protocol=Protocol.A2A, agent_uri="https://test-agent.adcontextprotocol.org" ) ``` """ base_config = TEST_AGENT_MCP_CONFIG.model_dump() base_config.update(overrides) return AgentConfig(**base_config)Create a custom test agent configuration.
Useful when you need to modify the default test agent setup.
Args
**overrides- Keyword arguments to override default config values
Returns
Complete agent configuration
Example
from adcp.testing import create_test_agent from adcp.client import ADCPClient # Use default test agent with custom ID config = create_test_agent(id="my-test-agent") client = ADCPClient(config)Example
# Use A2A protocol instead of MCP from adcp.types.core import Protocol config = create_test_agent( protocol=Protocol.A2A, agent_uri="https://test-agent.adcontextprotocol.org" ) def domain_matches(property_domain: str, agent_domain_pattern: str) ‑> bool-
Expand source code
def domain_matches(property_domain: str, agent_domain_pattern: str) -> bool: """Check if domains match per AdCP rules. Rules: - Exact match always succeeds - 'example.com' matches www.example.com, m.example.com (common subdomains) - 'subdomain.example.com' matches that specific subdomain only - '*.example.com' matches all subdomains Args: property_domain: Domain from property agent_domain_pattern: Domain pattern from adagents.json Returns: True if domains match per AdCP rules """ # Normalize both domains for comparison try: property_domain = _normalize_domain(property_domain) agent_domain_pattern = _normalize_domain(agent_domain_pattern) except AdagentsValidationError: # Invalid domain format - no match return False # Exact match if property_domain == agent_domain_pattern: return True # Wildcard pattern (*.example.com) if agent_domain_pattern.startswith("*."): base_domain = agent_domain_pattern[2:] return property_domain.endswith(f".{base_domain}") # Bare domain matches common subdomains (www, m) # If agent pattern is a bare domain (no subdomain), match www/m subdomains if "." in agent_domain_pattern and not agent_domain_pattern.startswith("www."): # Check if this looks like a bare domain (e.g., example.com) parts = agent_domain_pattern.split(".") if len(parts) == 2: # Looks like bare domain common_subdomains = ["www", "m"] for subdomain in common_subdomains: if property_domain == f"{subdomain}.{agent_domain_pattern}": return True return FalseCheck if domains match per AdCP rules.
Rules: - Exact match always succeeds - 'example.com' matches www.example.com, m.example.com (common subdomains) - 'subdomain.example.com' matches that specific subdomain only - '*.example.com' matches all subdomains
Args
property_domain- Domain from property
agent_domain_pattern- Domain pattern from adagents.json
Returns
True if domains match per AdCP rules
def extract_webhook_result_data(webhook_payload: dict[str, Any]) ‑> AdcpAsyncResponseData | None-
Expand source code
def extract_webhook_result_data(webhook_payload: dict[str, Any]) -> AdcpAsyncResponseData | None: """ Extract result data from webhook payload (MCP or A2A format). This utility function handles webhook payloads from both MCP and A2A protocols, extracting the result data regardless of the webhook format. Useful for quick inspection, logging, or custom webhook routing logic without requiring full client initialization. Protocol Detection: - A2A Task: Has "artifacts" field (terminated statuses: completed, failed) - A2A TaskStatusUpdateEvent: Has nested "status.message" structure (intermediate statuses) - MCP: Has "result" field directly Args: webhook_payload: Raw webhook dictionary from HTTP request (JSON-deserialized) Returns: AdcpAsyncResponseData union type containing the extracted AdCP response, or None if no result present. For A2A webhooks, unwraps data from artifacts/message parts structure. For MCP webhooks, returns the result field directly. Examples: Extract from MCP webhook: >>> mcp_payload = { ... "task_id": "task_123", ... "task_type": "create_media_buy", ... "status": "completed", ... "timestamp": "2025-01-15T10:00:00Z", ... "result": {"media_buy_id": "mb_123", "buyer_ref": "ref_123", "packages": []} ... } >>> result = extract_webhook_result_data(mcp_payload) >>> print(result["media_buy_id"]) mb_123 Extract from A2A Task webhook: >>> a2a_task_payload = { ... "id": "task_456", ... "context_id": "ctx_456", ... "status": {"state": "completed", "timestamp": "2025-01-15T10:00:00Z"}, ... "artifacts": [ ... { ... "artifact_id": "artifact_456", ... "parts": [ ... { ... "data": { ... "media_buy_id": "mb_456", ... "buyer_ref": "ref_456", ... "packages": [] ... } ... } ... ] ... } ... ] ... } >>> result = extract_webhook_result_data(a2a_task_payload) >>> print(result["media_buy_id"]) mb_456 Extract from A2A TaskStatusUpdateEvent webhook: >>> a2a_event_payload = { ... "task_id": "task_789", ... "context_id": "ctx_789", ... "status": { ... "state": "working", ... "timestamp": "2025-01-15T10:00:00Z", ... "message": { ... "message_id": "msg_789", ... "role": "agent", ... "parts": [ ... {"data": {"current_step": "processing", "percentage": 50}} ... ] ... } ... }, ... "final": False ... } >>> result = extract_webhook_result_data(a2a_event_payload) >>> print(result["percentage"]) 50 Handle webhook with no result: >>> empty_payload = {"task_id": "task_000", "status": "working", "timestamp": "..."} >>> result = extract_webhook_result_data(empty_payload) >>> print(result) None """ # Detect A2A Task format (has "artifacts" field) if "artifacts" in webhook_payload: # Extract from task.artifacts[].parts[] artifacts = webhook_payload.get("artifacts", []) if not artifacts: return None # Use last artifact (most recent) target_artifact = artifacts[-1] parts = target_artifact.get("parts", []) if not parts: return None # Find DataPart (skip TextPart) for part in parts: # Check if this part has "data" field (DataPart) if "data" in part: data = part["data"] # Unwrap {"response": {...}} wrapper if present (A2A convention) if isinstance(data, dict) and "response" in data and len(data) == 1: return cast(AdcpAsyncResponseData, data["response"]) return cast(AdcpAsyncResponseData, data) return None # Detect A2A TaskStatusUpdateEvent format (has nested "status.message") status = webhook_payload.get("status") if isinstance(status, dict): message = status.get("message") if isinstance(message, dict): # Extract from status.message.parts[] parts = message.get("parts", []) if not parts: return None # Find DataPart for part in parts: if "data" in part: data = part["data"] # Unwrap {"response": {...}} wrapper if present if isinstance(data, dict) and "response" in data and len(data) == 1: return cast(AdcpAsyncResponseData, data["response"]) return cast(AdcpAsyncResponseData, data) return None # MCP format: result field directly return cast(AdcpAsyncResponseData | None, webhook_payload.get("result"))Extract result data from webhook payload (MCP or A2A format).
This utility function handles webhook payloads from both MCP and A2A protocols, extracting the result data regardless of the webhook format. Useful for quick inspection, logging, or custom webhook routing logic without requiring full client initialization.
Protocol Detection: - A2A Task: Has "artifacts" field (terminated statuses: completed, failed) - A2A TaskStatusUpdateEvent: Has nested "status.message" structure (intermediate statuses) - MCP: Has "result" field directly
Args
webhook_payload- Raw webhook dictionary from HTTP request (JSON-deserialized)
Returns
AdcpAsyncResponseData union type containing the extracted AdCP response, or None if no result present. For A2A webhooks, unwraps data from artifacts/message parts structure. For MCP webhooks, returns the result field directly.
Examples
Extract from MCP webhook:
>>> mcp_payload = { ... "task_id": "task_123", ... "task_type": "create_media_buy", ... "status": "completed", ... "timestamp": "2025-01-15T10:00:00Z", ... "result": {"media_buy_id": "mb_123", "buyer_ref": "ref_123", "packages": []} ... } >>> result = extract_webhook_result_data(mcp_payload) >>> print(result["media_buy_id"]) mb_123Extract from A2A Task webhook:
>>> a2a_task_payload = { ... "id": "task_456", ... "context_id": "ctx_456", ... "status": {"state": "completed", "timestamp": "2025-01-15T10:00:00Z"}, ... "artifacts": [ ... { ... "artifact_id": "artifact_456", ... "parts": [ ... { ... "data": { ... "media_buy_id": "mb_456", ... "buyer_ref": "ref_456", ... "packages": [] ... } ... } ... ] ... } ... ] ... } >>> result = extract_webhook_result_data(a2a_task_payload) >>> print(result["media_buy_id"]) mb_456Extract from A2A TaskStatusUpdateEvent webhook:
>>> a2a_event_payload = { ... "task_id": "task_789", ... "context_id": "ctx_789", ... "status": { ... "state": "working", ... "timestamp": "2025-01-15T10:00:00Z", ... "message": { ... "message_id": "msg_789", ... "role": "agent", ... "parts": [ ... {"data": {"current_step": "processing", "percentage": 50}} ... ] ... } ... }, ... "final": False ... } >>> result = extract_webhook_result_data(a2a_event_payload) >>> print(result["percentage"]) 50Handle webhook with no result:
>>> empty_payload = {"task_id": "task_000", "status": "working", "timestamp": "..."} >>> result = extract_webhook_result_data(empty_payload) >>> print(result) None async def fetch_adagents(publisher_domain: str,
timeout: float = 10.0,
user_agent: str = 'AdCP-Client/1.0',
client: httpx.AsyncClient | None = None) ‑> dict[str, typing.Any]-
Expand source code
async def fetch_adagents( publisher_domain: str, timeout: float = 10.0, user_agent: str = "AdCP-Client/1.0", client: httpx.AsyncClient | None = None, ) -> dict[str, Any]: """Fetch and parse adagents.json from publisher domain. Follows authoritative_location redirects per the AdCP specification. When a publisher's adagents.json contains an authoritative_location field instead of authorized_agents, this function fetches the referenced URL to get the actual authorization data. Args: publisher_domain: Domain hosting the adagents.json file timeout: Request timeout in seconds user_agent: User-Agent header for HTTP request client: Optional httpx.AsyncClient for connection pooling. If provided, caller is responsible for client lifecycle. If None, a new client is created for this request. Returns: Parsed adagents.json data (resolved from authoritative_location if present) Raises: AdagentsNotFoundError: If adagents.json not found (404) AdagentsValidationError: If JSON is invalid, malformed, or redirects exceed maximum depth or form a loop AdagentsTimeoutError: If request times out Notes: For production use with multiple requests, pass a shared httpx.AsyncClient to enable connection pooling and improve performance. """ # Validate and normalize domain for security publisher_domain = _validate_publisher_domain(publisher_domain) # Construct initial URL url = f"https://{publisher_domain}/.well-known/adagents.json" # Track visited URLs to detect loops visited_urls: set[str] = set() for depth in range(MAX_REDIRECT_DEPTH + 1): # Check for redirect loop if url in visited_urls: raise AdagentsValidationError(f"Circular redirect detected: {url} already visited") visited_urls.add(url) data = await _fetch_adagents_url(url, timeout, user_agent, client) # Check if this is a redirect. A response with authoritative_location but no # authorized_agents indicates a redirect. If both are present, authorized_agents # takes precedence (response is treated as final). if "authoritative_location" in data and "authorized_agents" not in data: authoritative_url = data["authoritative_location"] # Validate HTTPS requirement if not isinstance(authoritative_url, str) or not authoritative_url.startswith( "https://" ): raise AdagentsValidationError( f"authoritative_location must be an HTTPS URL, got: {authoritative_url!r}" ) # Check if we've exceeded max depth if depth >= MAX_REDIRECT_DEPTH: raise AdagentsValidationError( f"Maximum redirect depth ({MAX_REDIRECT_DEPTH}) exceeded" ) # Follow the redirect url = authoritative_url continue # We have the final data with authorized_agents (or both fields present, # in which case authorized_agents takes precedence) return data # Unreachable: loop always exits via return or raise above raise AssertionError("Unreachable") # pragma: no coverFetch and parse adagents.json from publisher domain.
Follows authoritative_location redirects per the AdCP specification. When a publisher's adagents.json contains an authoritative_location field instead of authorized_agents, this function fetches the referenced URL to get the actual authorization data.
Args
publisher_domain- Domain hosting the adagents.json file
timeout- Request timeout in seconds
user_agent- User-Agent header for HTTP request
client- Optional httpx.AsyncClient for connection pooling. If provided, caller is responsible for client lifecycle. If None, a new client is created for this request.
Returns
Parsed adagents.json data (resolved from authoritative_location if present)
Raises
AdagentsNotFoundError- If adagents.json not found (404)
AdagentsValidationError- If JSON is invalid, malformed, or redirects exceed maximum depth or form a loop
AdagentsTimeoutError- If request times out
Notes
For production use with multiple requests, pass a shared httpx.AsyncClient to enable connection pooling and improve performance.
-
Expand source code
async def fetch_agent_authorizations( agent_url: str, publisher_domains: list[str], timeout: float = 10.0, client: httpx.AsyncClient | None = None, ) -> dict[str, AuthorizationContext]: """Fetch authorization contexts by checking publisher adagents.json files. This function discovers what publishers have authorized your agent by fetching their adagents.json files from the .well-known directory and extracting the properties your agent can access. This is the "pull" approach - you query publishers to see if they've authorized you. Args: agent_url: URL of your sales agent publisher_domains: List of publisher domains to check (e.g., ["nytimes.com", "wsj.com"]) timeout: Request timeout in seconds for each fetch client: Optional httpx.AsyncClient for connection pooling Returns: Dictionary mapping publisher domain to AuthorizationContext. Only includes domains where the agent is authorized. Example: >>> # "Pull" approach - check what publishers have authorized you >>> contexts = await fetch_agent_authorizations( ... "https://our-sales-agent.com", ... ["nytimes.com", "wsj.com", "cnn.com"] ... ) >>> for domain, ctx in contexts.items(): ... print(f"{domain}:") ... print(f" Property IDs: {ctx.property_ids}") ... print(f" Tags: {ctx.property_tags}") Notes: - Silently skips domains where adagents.json is not found or invalid - Only returns domains where the agent is explicitly authorized - For production use with many domains, pass a shared httpx.AsyncClient to enable connection pooling """ import asyncio # Create tasks to fetch all adagents.json files in parallel async def fetch_authorization_for_domain( domain: str, ) -> tuple[str, AuthorizationContext | None]: """Fetch authorization context for a single domain.""" try: adagents_data = await fetch_adagents(domain, timeout=timeout, client=client) # Check if agent is authorized if not verify_agent_authorization(adagents_data, agent_url): return (domain, None) # Get properties for this agent properties = get_properties_by_agent(adagents_data, agent_url) # Create authorization context return (domain, AuthorizationContext(properties)) except (AdagentsNotFoundError, AdagentsValidationError, AdagentsTimeoutError): # Silently skip domains with missing or invalid adagents.json return (domain, None) # Fetch all domains in parallel tasks = [fetch_authorization_for_domain(domain) for domain in publisher_domains] results = await asyncio.gather(*tasks) # Build result dictionary, filtering out None values return {domain: ctx for domain, ctx in results if ctx is not None}Fetch authorization contexts by checking publisher adagents.json files.
This function discovers what publishers have authorized your agent by fetching their adagents.json files from the .well-known directory and extracting the properties your agent can access.
This is the "pull" approach - you query publishers to see if they've authorized you.
Args
agent_url- URL of your sales agent
publisher_domains- List of publisher domains to check (e.g., ["nytimes.com", "wsj.com"])
timeout- Request timeout in seconds for each fetch
client- Optional httpx.AsyncClient for connection pooling
Returns
Dictionary mapping publisher domain to AuthorizationContext. Only includes domains where the agent is authorized.
Example
>>> # "Pull" approach - check what publishers have authorized you >>> contexts = await fetch_agent_authorizations( ... "https://our-sales-agent.com", ... ["nytimes.com", "wsj.com", "cnn.com"] ... ) >>> for domain, ctx in contexts.items(): ... print(f"{domain}:") ... print(f" Property IDs: {ctx.property_ids}") ... print(f" Tags: {ctx.property_tags}")Notes
- Silently skips domains where adagents.json is not found or invalid
- Only returns domains where the agent is explicitly authorized
- For production use with many domains, pass a shared httpx.AsyncClient to enable connection pooling
def get_adcp_signed_headers_for_webhook(headers: dict[str, Any],
secret: str,
timestamp: str,
payload: dict[str, Any] | AdCPBaseModel) ‑> dict[str, typing.Any]-
Expand source code
def get_adcp_signed_headers_for_webhook( headers: dict[str, Any], secret: str, timestamp: str, payload: dict[str, Any] | AdCPBaseModel ) -> dict[str, Any]: """ Generate AdCP-compliant signed headers for webhook delivery. This function creates a cryptographic signature that proves the webhook came from an authorized agent and protects against replay attacks by including a timestamp in the signed message. The function adds two headers to the provided headers dict: - X-AdCP-Signature: HMAC-SHA256 signature in format "sha256=<hex_digest>" - X-AdCP-Timestamp: ISO 8601 timestamp used in signature generation The signing algorithm: 1. Constructs message as "{timestamp}.{json_payload}" 2. JSON-serializes payload with compact separators (no sorted keys for performance) 3. UTF-8 encodes the message 4. HMAC-SHA256 signs with the shared secret 5. Hex-encodes and prefixes with "sha256=" Args: headers: Existing headers dictionary to add signature headers to secret: Shared secret key for HMAC signing timestamp: ISO 8601 timestamp string (e.g., "2025-01-15T10:00:00Z") payload: Webhook payload (dict or Pydantic model - will be JSON-serialized) Returns: The modified headers dictionary with signature headers added Examples: Sign and send an MCP webhook: >>> from adcp.webhooks import create_mcp_webhook_payload get_adcp_signed_headers_for_webhook >>> from datetime import datetime, timezone >>> >>> payload = create_mcp_webhook_payload( ... task_id="task_123", ... task_type="get_products", ... status="completed", ... result={"products": [...]} ... ) >>> headers = {"Content-Type": "application/json"} >>> timestamp = datetime.now(timezone.utc).isoformat() >>> signed_headers = get_adcp_signed_headers_for_webhook( ... headers, secret="my-webhook-secret", timestamp=timestamp, payload=payload ... ) >>> >>> # Send webhook with signed headers >>> import httpx >>> response = await httpx.post( ... webhook_url, ... json=payload, ... headers=signed_headers ... ) Headers will contain: >>> print(signed_headers) { "Content-Type": "application/json", "X-AdCP-Signature": "sha256=a1b2c3...", "X-AdCP-Timestamp": "2025-01-15T10:00:00Z" } Sign with Pydantic model directly: >>> from adcp import GetMediaBuyDeliveryResponse >>> from datetime import datetime, timezone >>> >>> response: GetMediaBuyDeliveryResponse = ... # From API call >>> headers = {"Content-Type": "application/json"} >>> timestamp = datetime.now(timezone.utc).isoformat() >>> signed_headers = get_adcp_signed_headers_for_webhook( ... headers, secret="my-webhook-secret", timestamp=timestamp, payload=response ... ) >>> # Pydantic model is automatically converted to dict for signing """ # Convert Pydantic model to dict if needed # All AdCP types inherit from AdCPBaseModel (Pydantic BaseModel) if hasattr(payload, "model_dump"): payload_dict = payload.model_dump(mode="json") else: payload_dict = payload # Serialize payload to JSON with consistent formatting # Note: sort_keys=False for performance (key order doesn't affect signature) payload_bytes = json.dumps(payload_dict, separators=(",", ":"), sort_keys=False).encode("utf-8") # Construct signed message: timestamp.payload # Including timestamp prevents replay attacks signed_message = f"{timestamp}.{payload_bytes.decode('utf-8')}" # Generate HMAC-SHA256 signature over timestamp + payload signature_hex = hmac.new( secret.encode("utf-8"), signed_message.encode("utf-8"), hashlib.sha256 ).hexdigest() # Add AdCP-compliant signature headers headers["X-AdCP-Signature"] = f"sha256={signature_hex}" headers["X-AdCP-Timestamp"] = timestamp return headersGenerate AdCP-compliant signed headers for webhook delivery.
This function creates a cryptographic signature that proves the webhook came from an authorized agent and protects against replay attacks by including a timestamp in the signed message.
The function adds two headers to the provided headers dict: - X-AdCP-Signature: HMAC-SHA256 signature in format "sha256=
" - X-AdCP-Timestamp: ISO 8601 timestamp used in signature generation The signing algorithm: 1. Constructs message as "{timestamp}.{json_payload}" 2. JSON-serializes payload with compact separators (no sorted keys for performance) 3. UTF-8 encodes the message 4. HMAC-SHA256 signs with the shared secret 5. Hex-encodes and prefixes with "sha256="
Args
headers- Existing headers dictionary to add signature headers to
secret- Shared secret key for HMAC signing
timestamp- ISO 8601 timestamp string (e.g., "2025-01-15T10:00:00Z")
payload- Webhook payload (dict or Pydantic model - will be JSON-serialized)
Returns
The modified headers dictionary with signature headers added
Examples
Sign and send an MCP webhook:
>>> from adcp.webhooks import create_mcp_webhook_payload get_adcp_signed_headers_for_webhook >>> from datetime import datetime, timezone >>> >>> payload = create_mcp_webhook_payload( ... task_id="task_123", ... task_type="get_products", ... status="completed", ... result={"products": [...]} ... ) >>> headers = {"Content-Type": "application/json"} >>> timestamp = datetime.now(timezone.utc).isoformat() >>> signed_headers = get_adcp_signed_headers_for_webhook( ... headers, secret="my-webhook-secret", timestamp=timestamp, payload=payload ... ) >>> >>> # Send webhook with signed headers >>> import httpx >>> response = await httpx.post( ... webhook_url, ... json=payload, ... headers=signed_headers ... )Headers will contain:
>>> print(signed_headers) { "Content-Type": "application/json", "X-AdCP-Signature": "sha256=a1b2c3...", "X-AdCP-Timestamp": "2025-01-15T10:00:00Z" }Sign with Pydantic model directly:
>>> from adcp import GetMediaBuyDeliveryResponse >>> from datetime import datetime, timezone >>> >>> response: GetMediaBuyDeliveryResponse = ... # From API call >>> headers = {"Content-Type": "application/json"} >>> timestamp = datetime.now(timezone.utc).isoformat() >>> signed_headers = get_adcp_signed_headers_for_webhook( ... headers, secret="my-webhook-secret", timestamp=timestamp, payload=response ... ) >>> # Pydantic model is automatically converted to dict for signing def get_adcp_version() ‑> str-
Expand source code
def get_adcp_version() -> str: """ Get the target AdCP specification version this SDK is built for. This version determines which AdCP schemas are used for type generation and validation. The SDK is designed to work with this specific version of the AdCP specification. Returns: AdCP specification version (e.g., "2.5.0") Raises: FileNotFoundError: If ADCP_VERSION file is missing from package """ from importlib.resources import files # Read from ADCP_VERSION file in package version_file = files("adcp") / "ADCP_VERSION" return version_file.read_text().strip()Get the target AdCP specification version this SDK is built for.
This version determines which AdCP schemas are used for type generation and validation. The SDK is designed to work with this specific version of the AdCP specification.
Returns
AdCP specification version (e.g., "2.5.0")
Raises
FileNotFoundError- If ADCP_VERSION file is missing from package
def get_all_properties(adagents_data: dict[str, Any]) ‑> list[dict[str, typing.Any]]-
Expand source code
def get_all_properties(adagents_data: dict[str, Any]) -> list[dict[str, Any]]: """Extract all properties from adagents.json data. Args: adagents_data: Parsed adagents.json data Returns: List of all properties across all authorized agents, with agent_url added Raises: AdagentsValidationError: If adagents_data is malformed """ if not isinstance(adagents_data, dict): raise AdagentsValidationError("adagents_data must be a dictionary") authorized_agents = adagents_data.get("authorized_agents") if not isinstance(authorized_agents, list): raise AdagentsValidationError("adagents.json must have 'authorized_agents' array") properties = [] for agent in authorized_agents: if not isinstance(agent, dict): continue agent_url = agent.get("url", "") if not agent_url: continue agent_properties = agent.get("properties", []) if not isinstance(agent_properties, list): continue # Add each property with the agent URL for reference for prop in agent_properties: if isinstance(prop, dict): # Create a copy and add agent_url prop_with_agent = {**prop, "agent_url": agent_url} properties.append(prop_with_agent) return propertiesExtract all properties from adagents.json data.
Args
adagents_data- Parsed adagents.json data
Returns
List of all properties across all authorized agents, with agent_url added
Raises
AdagentsValidationError- If adagents_data is malformed
-
Expand source code
def get_all_tags(adagents_data: dict[str, Any]) -> set[str]: """Extract all unique tags from properties in adagents.json data. Args: adagents_data: Parsed adagents.json data Returns: Set of all unique tags across all properties Raises: AdagentsValidationError: If adagents_data is malformed """ properties = get_all_properties(adagents_data) tags = set() for prop in properties: prop_tags = prop.get("tags", []) if isinstance(prop_tags, list): for tag in prop_tags: if isinstance(tag, str): tags.add(tag) return tagsExtract all unique tags from properties in adagents.json data.
Args
adagents_data- Parsed adagents.json data
Returns
Set of all unique tags across all properties
Raises
AdagentsValidationError- If adagents_data is malformed
def get_asset_count(format: Format) ‑> int-
Expand source code
def get_asset_count(format: Format) -> int: """Get the count of assets in a format (for display purposes). Args: format: The Format object Returns: Number of assets, or 0 if none defined """ return len(get_format_assets(format))Get the count of assets in a format (for display purposes).
Args
format- The Format object
Returns
Number of assets, or 0 if none defined
def get_format_assets(format: Format) ‑> list[FormatAsset]-
Expand source code
def get_format_assets(format: Format) -> list[FormatAsset]: """Get assets from a Format. Returns the list of assets from the format's `assets` field. Returns empty list if no assets are defined (flexible format with no assets). Args: format: The Format object from list_creative_formats response Returns: List of assets Example: ```python formats = await agent.simple.list_creative_formats() for format in formats.formats: assets = get_format_assets(format) print(f"{format.name} has {len(assets)} assets") ``` """ if format.assets and len(format.assets) > 0: return list(format.assets) return []Get assets from a Format.
Returns the list of assets from the format's
assetsfield. Returns empty list if no assets are defined (flexible format with no assets).Args
format- The Format object from list_creative_formats response
Returns
List of assets
Example
formats = await agent.simple.list_creative_formats() for format in formats.formats: assets = get_format_assets(format) print(f"{format.name} has {len(assets)} assets") def get_individual_assets(format: Format) ‑> list[FormatAsset]-
Expand source code
def get_individual_assets(format: Format) -> list[FormatAsset]: """Get individual assets (not repeatable groups) from a Format. Args: format: The Format object Returns: List of individual assets (item_type='individual') """ return [asset for asset in get_format_assets(format) if _get_item_type(asset) == "individual"]Get individual assets (not repeatable groups) from a Format.
Args
format- The Format object
Returns
List of individual assets (item_type='individual')
def get_optional_assets(format: Format) ‑> list[FormatAsset]-
Expand source code
def get_optional_assets(format: Format) -> list[FormatAsset]: """Get only optional assets from a Format. Note: When using deprecated `assets_required`, this will always return empty since assets_required only contained required assets. Args: format: The Format object Returns: List of optional assets only Example: ```python optional_assets = get_optional_assets(format) print(f"Can optionally provide {len(optional_assets)} additional assets") ``` """ return [asset for asset in get_format_assets(format) if not _is_required(asset)]Get only optional assets from a Format.
Note: When using deprecated
assets_required, this will always return empty since assets_required only contained required assets.Args
format- The Format object
Returns
List of optional assets only
Example
optional_assets = get_optional_assets(format) print(f"Can optionally provide {len(optional_assets)} additional assets") def get_properties_by_agent(adagents_data: dict[str, Any], agent_url: str) ‑> list[dict[str, typing.Any]]-
Expand source code
def get_properties_by_agent(adagents_data: dict[str, Any], agent_url: str) -> list[dict[str, Any]]: """Get all properties authorized for a specific agent. Handles all authorization types per the AdCP specification: - inline_properties: Properties defined directly in the agent's properties array - property_ids: Filter top-level properties by property_id - property_tags: Filter top-level properties by tags - publisher_properties: References properties from other publisher domains (returns the selector objects, not resolved properties) Args: adagents_data: Parsed adagents.json data agent_url: URL of the agent to filter by Returns: List of properties for the specified agent (empty if agent not found) Raises: AdagentsValidationError: If adagents_data is malformed """ if not isinstance(adagents_data, dict): raise AdagentsValidationError("adagents_data must be a dictionary") authorized_agents = adagents_data.get("authorized_agents") if not isinstance(authorized_agents, list): raise AdagentsValidationError("adagents.json must have 'authorized_agents' array") # Get top-level properties for reference-based authorization types top_level_properties = adagents_data.get("properties", []) if not isinstance(top_level_properties, list): top_level_properties = [] # Normalize the agent URL for comparison normalized_agent_url = normalize_url(agent_url) for agent in authorized_agents: if not isinstance(agent, dict): continue agent_url_from_json = agent.get("url", "") if not agent_url_from_json: continue # Match agent URL (protocol-agnostic) if normalize_url(agent_url_from_json) != normalized_agent_url: continue # Found the agent - determine authorization type authorization_type = agent.get("authorization_type", "") # Handle inline_properties (properties array directly on agent) if authorization_type == "inline_properties" or "properties" in agent: properties = agent.get("properties", []) if not isinstance(properties, list): return [] return [p for p in properties if isinstance(p, dict)] # Handle property_ids (filter top-level properties by property_id) if authorization_type == "property_ids": authorized_ids = set(agent.get("property_ids", [])) return [ p for p in top_level_properties if isinstance(p, dict) and p.get("property_id") in authorized_ids ] # Handle property_tags (filter top-level properties by tags) if authorization_type == "property_tags": authorized_tags = set(agent.get("property_tags", [])) return [ p for p in top_level_properties if isinstance(p, dict) and set(p.get("tags", [])) & authorized_tags ] # Handle publisher_properties (cross-domain references) # Returns the selector objects; caller must resolve against other domains if authorization_type == "publisher_properties": publisher_props = agent.get("publisher_properties", []) if not isinstance(publisher_props, list): return [] return [p for p in publisher_props if isinstance(p, dict)] # No recognized authorization type - return empty return [] return []Get all properties authorized for a specific agent.
Handles all authorization types per the AdCP specification: - inline_properties: Properties defined directly in the agent's properties array - property_ids: Filter top-level properties by property_id - property_tags: Filter top-level properties by tags - publisher_properties: References properties from other publisher domains (returns the selector objects, not resolved properties)
Args
adagents_data- Parsed adagents.json data
agent_url- URL of the agent to filter by
Returns
List of properties for the specified agent (empty if agent not found)
Raises
AdagentsValidationError- If adagents_data is malformed
def get_repeatable_groups(format: Format) ‑> list[FormatAsset]-
Expand source code
def get_repeatable_groups(format: Format) -> list[FormatAsset]: """Get repeatable asset groups from a Format. Args: format: The Format object Returns: List of repeatable asset groups (item_type='repeatable_group') """ return [ asset for asset in get_format_assets(format) if _get_item_type(asset) == "repeatable_group" ]Get repeatable asset groups from a Format.
Args
format- The Format object
Returns
List of repeatable asset groups (item_type='repeatable_group')
def get_required_assets(format: Format) ‑> list[FormatAsset]-
Expand source code
def get_required_assets(format: Format) -> list[FormatAsset]: """Get only required assets from a Format. Args: format: The Format object Returns: List of required assets only Example: ```python required_assets = get_required_assets(format) print(f"Must provide {len(required_assets)} assets") ``` """ return [asset for asset in get_format_assets(format) if _is_required(asset)]Get only required assets from a Format.
Args
format- The Format object
Returns
List of required assets only
Example
required_assets = get_required_assets(format) print(f"Must provide {len(required_assets)} assets") def has_assets(format: Format) ‑> bool-
Expand source code
def has_assets(format: Format) -> bool: """Check if a format has any assets defined. Args: format: The Format object Returns: True if format has assets, False otherwise """ return get_asset_count(format) > 0Check if a format has any assets defined.
Args
format- The Format object
Returns
True if format has assets, False otherwise
def identifiers_match(property_identifiers: list[dict[str, str]],
agent_identifiers: list[dict[str, str]]) ‑> bool-
Expand source code
def identifiers_match( property_identifiers: list[dict[str, str]], agent_identifiers: list[dict[str, str]], ) -> bool: """Check if any property identifier matches agent's authorized identifiers. Args: property_identifiers: Identifiers from property (e.g., [{"type": "domain", "value": "cnn.com"}]) agent_identifiers: Identifiers from adagents.json Returns: True if any identifier matches Notes: - Domain identifiers use AdCP domain matching rules - Other identifiers (bundle_id, roku_store_id, etc.) require exact match """ for prop_id in property_identifiers: prop_type = prop_id.get("type", "") prop_value = prop_id.get("value", "") for agent_id in agent_identifiers: agent_type = agent_id.get("type", "") agent_value = agent_id.get("value", "") # Type must match if prop_type != agent_type: continue # Domain identifiers use special matching rules if prop_type == "domain": if domain_matches(prop_value, agent_value): return True else: # Other identifier types require exact match if prop_value == agent_value: return True return FalseCheck if any property identifier matches agent's authorized identifiers.
Args
property_identifiers- Identifiers from property (e.g., [{"type": "domain", "value": "cnn.com"}])
agent_identifiers- Identifiers from adagents.json
Returns
True if any identifier matches
Notes
- Domain identifiers use AdCP domain matching rules
- Other identifiers (bundle_id, roku_store_id, etc.) require exact match
def normalize_assets_required(assets_required: list[Any]) ‑> list[typing.Any]-
Expand source code
def normalize_assets_required(assets_required: list[Any]) -> list[FormatAsset]: """Convert deprecated assets_required to new assets format. .. deprecated:: 3.2.0 The ``assets_required`` field was removed in ADCP 3.0.0-beta.2. This function will be removed in a future version. All assets in assets_required are required by definition (that's why they were in that array). The new `assets` field has an explicit `required: boolean` to allow both required AND optional assets. Args: assets_required: The deprecated assets_required array Returns: Normalized assets as Pydantic models with explicit required=True """ warnings.warn( "normalize_assets_required() is deprecated. " "The assets_required field was removed in ADCP 3.0.0-beta.2. " "This function will be removed in a future version.", DeprecationWarning, stacklevel=2, ) normalized: list[FormatAsset] = [] for asset in assets_required: # Get asset data as dict if isinstance(asset, dict): asset_dict = asset else: asset_dict = asset.model_dump() if hasattr(asset, "model_dump") else dict(asset) # Map old fields to new schema format mapped = {**asset_dict, "required": True} # Ensure asset_id is present (map from asset_group_id if needed) if "asset_group_id" in mapped and "asset_id" not in mapped: mapped["asset_id"] = mapped.pop("asset_group_id") # Remove fields that don't exist in the new schema for old_field in ("min_count", "max_count", "assets"): mapped.pop(old_field, None) # Use AssetsModel (individual asset type) normalized.append(AssetsModel(**mapped)) return normalizedConvert deprecated assets_required to new assets format.
Deprecated since version: 3.2.0
The
assets_requiredfield was removed in ADCP 3.0.0-beta.2. This function will be removed in a future version.All assets in assets_required are required by definition (that's why they were in that array). The new
assetsfield has an explicitrequired: booleanto allow both required AND optional assets.Args
assets_required- The deprecated assets_required array
Returns
Normalized assets as Pydantic models with explicit required=True
def uses_deprecated_assets_field(format: Format) ‑> bool-
Expand source code
def uses_deprecated_assets_field(format: Format) -> bool: """Check if format uses deprecated assets_required field. .. deprecated:: 3.2.0 The ``assets_required`` field was removed in ADCP 3.0.0-beta.2. This function always returns False and will be removed in a future version. Args: format: The Format object Returns: Always False (deprecated field no longer exists) """ warnings.warn( "uses_deprecated_assets_field() is deprecated and always returns False. " "The assets_required field was removed in ADCP 3.0.0-beta.2. " "This function will be removed in a future version.", DeprecationWarning, stacklevel=2, ) return FalseCheck if format uses deprecated assets_required field.
Deprecated since version: 3.2.0
The
assets_requiredfield was removed in ADCP 3.0.0-beta.2. This function always returns False and will be removed in a future version.Args
format- The Format object
Returns
Always False (deprecated field no longer exists)
def validate_adagents(adagents: dict[str, Any]) ‑> None-
Expand source code
def validate_adagents(adagents: dict[str, Any]) -> None: """Validate an adagents.json structure. Args: adagents: The adagents.json dict Raises: ValidationError: If validation fails """ if "agents" in adagents: for agent in adagents["agents"]: validate_agent_authorization(agent)Validate an adagents.json structure.
Args
adagents- The adagents.json dict
Raises
ValidationError- If validation fails
-
Expand source code
def validate_agent_authorization(agent: dict[str, Any]) -> None: """Validate agent authorization discriminated union. AdCP v2.4.0+ uses discriminated unions with authorization_type discriminator: - authorization_type: "property_ids" requires property_ids - authorization_type: "property_tags" requires property_tags - authorization_type: "inline_properties" requires properties - authorization_type: "publisher_properties" requires publisher_properties For backward compatibility, also validates the old mutual exclusivity constraint. Args: agent: An agent dict from adagents.json Raises: ValidationError: If discriminator or field constraints are violated """ authorization_type = agent.get("authorization_type") auth_fields = ["properties", "property_ids", "property_tags", "publisher_properties"] present_fields = [field for field in auth_fields if field in agent and agent[field] is not None] # If authorization_type discriminator is present, validate discriminated union if authorization_type: if authorization_type == "property_ids" and "property_ids" not in present_fields: raise ValidationError( "Agent with authorization_type='property_ids' must have property_ids" ) elif authorization_type == "property_tags" and "property_tags" not in present_fields: raise ValidationError( "Agent with authorization_type='property_tags' must have property_tags" ) elif authorization_type == "inline_properties" and "properties" not in present_fields: raise ValidationError( "Agent with authorization_type='inline_properties' must have properties" ) elif ( authorization_type == "publisher_properties" and "publisher_properties" not in present_fields ): raise ValidationError( "Agent with authorization_type='publisher_properties' " "must have publisher_properties" ) elif authorization_type not in ( "property_ids", "property_tags", "inline_properties", "publisher_properties", ): raise ValidationError(f"Agent has invalid authorization_type: {authorization_type}") # Validate mutual exclusivity (for both old and new formats) if len(present_fields) > 1: raise ValidationError( f"Agent authorization cannot have multiple fields: {', '.join(present_fields)}. " f"Only one of {', '.join(auth_fields)} is allowed." ) if len(present_fields) == 0: raise ValidationError( f"Agent authorization must have exactly one of: {', '.join(auth_fields)}." ) # If using publisher_properties, validate each item if "publisher_properties" in present_fields: for pub_prop in agent["publisher_properties"]: validate_publisher_properties_item(pub_prop)Validate agent authorization discriminated union.
AdCP v2.4.0+ uses discriminated unions with authorization_type discriminator: - authorization_type: "property_ids" requires property_ids - authorization_type: "property_tags" requires property_tags - authorization_type: "inline_properties" requires properties - authorization_type: "publisher_properties" requires publisher_properties
For backward compatibility, also validates the old mutual exclusivity constraint.
Args
agent- An agent dict from adagents.json
Raises
ValidationError- If discriminator or field constraints are violated
def validate_product(product: dict[str, Any]) ‑> None-
Expand source code
def validate_product(product: dict[str, Any]) -> None: """Validate a Product object. Args: product: Product dict Raises: ValidationError: If validation fails """ if "publisher_properties" in product and product["publisher_properties"]: for item in product["publisher_properties"]: validate_publisher_properties_item(item) def validate_publisher_properties_item(item: dict[str, Any]) ‑> None-
Expand source code
def validate_publisher_properties_item(item: dict[str, Any]) -> None: """Validate publisher_properties item discriminated union. AdCP v2.4.0+ uses discriminated unions with selection_type discriminator: - selection_type: "by_id" requires property_ids - selection_type: "by_tag" requires property_tags For backward compatibility, also validates the old mutual exclusivity constraint. Args: item: A single item from publisher_properties array Raises: ValidationError: If discriminator or field constraints are violated """ selection_type = item.get("selection_type") has_property_ids = "property_ids" in item and item["property_ids"] is not None has_property_tags = "property_tags" in item and item["property_tags"] is not None # If selection_type discriminator is present, validate discriminated union if selection_type: if selection_type == "by_id" and not has_property_ids: raise ValidationError( "publisher_properties item with selection_type='by_id' must have property_ids" ) elif selection_type == "by_tag" and not has_property_tags: raise ValidationError( "publisher_properties item with selection_type='by_tag' must have property_tags" ) elif selection_type not in ("by_id", "by_tag"): raise ValidationError( f"publisher_properties item has invalid selection_type: {selection_type}" ) # Validate mutual exclusivity (for both old and new formats) if has_property_ids and has_property_tags: raise ValidationError( "publisher_properties item cannot have both property_ids and property_tags. " "These fields are mutually exclusive." ) if not has_property_ids and not has_property_tags: raise ValidationError( "publisher_properties item must have either property_ids or property_tags. " "At least one is required." )Validate publisher_properties item discriminated union.
AdCP v2.4.0+ uses discriminated unions with selection_type discriminator: - selection_type: "by_id" requires property_ids - selection_type: "by_tag" requires property_tags
For backward compatibility, also validates the old mutual exclusivity constraint.
Args
item- A single item from publisher_properties array
Raises
ValidationError- If discriminator or field constraints are violated
-
Expand source code
def verify_agent_authorization( adagents_data: dict[str, Any], agent_url: str, property_type: str | None = None, property_identifiers: list[dict[str, str]] | None = None, ) -> bool: """Check if agent is authorized for a property. Args: adagents_data: Parsed adagents.json data agent_url: URL of the sales agent to verify property_type: Type of property (website, app, etc.) - optional property_identifiers: List of identifiers to match - optional Returns: True if agent is authorized, False otherwise Raises: AdagentsValidationError: If adagents_data is malformed Notes: - If property_type/identifiers are None, checks if agent is authorized for ANY property on this domain - Implements AdCP domain matching rules - Agent URLs are matched ignoring protocol and trailing slash """ # Validate structure if not isinstance(adagents_data, dict): raise AdagentsValidationError("adagents_data must be a dictionary") authorized_agents = adagents_data.get("authorized_agents") if not isinstance(authorized_agents, list): raise AdagentsValidationError("adagents.json must have 'authorized_agents' array") # Normalize the agent URL for comparison normalized_agent_url = normalize_url(agent_url) # Check each authorized agent for agent in authorized_agents: if not isinstance(agent, dict): continue agent_url_from_json = agent.get("url", "") if not agent_url_from_json: continue # Match agent URL (protocol-agnostic) if normalize_url(agent_url_from_json) != normalized_agent_url: continue # Found matching agent - now check properties properties = agent.get("properties") # If properties field is missing or empty, agent is authorized for all properties if properties is None or (isinstance(properties, list) and len(properties) == 0): return True # If no property filters specified, we found the agent - authorized if property_type is None and property_identifiers is None: return True # Check specific property authorization if isinstance(properties, list): for prop in properties: if not isinstance(prop, dict): continue # Check property type if specified if property_type is not None: prop_type = prop.get("property_type", "") if prop_type != property_type: continue # Check identifiers if specified if property_identifiers is not None: prop_identifiers = prop.get("identifiers", []) if not isinstance(prop_identifiers, list): continue if identifiers_match(property_identifiers, prop_identifiers): return True else: # Property type matched and no identifier check needed return True return FalseCheck if agent is authorized for a property.
Args
adagents_data- Parsed adagents.json data
agent_url- URL of the sales agent to verify
property_type- Type of property (website, app, etc.) - optional
property_identifiers- List of identifiers to match - optional
Returns
True if agent is authorized, False otherwise
Raises
AdagentsValidationError- If adagents_data is malformed
Notes
- If property_type/identifiers are None, checks if agent is authorized for ANY property on this domain
- Implements AdCP domain matching rules
- Agent URLs are matched ignoring protocol and trailing slash
async def verify_agent_for_property(publisher_domain: str,
agent_url: str,
property_identifiers: list[dict[str, str]],
property_type: str | None = None,
timeout: float = 10.0,
client: httpx.AsyncClient | None = None) ‑> bool-
Expand source code
async def verify_agent_for_property( publisher_domain: str, agent_url: str, property_identifiers: list[dict[str, str]], property_type: str | None = None, timeout: float = 10.0, client: httpx.AsyncClient | None = None, ) -> bool: """Convenience wrapper to fetch adagents.json and verify authorization in one call. Args: publisher_domain: Domain hosting the adagents.json file agent_url: URL of the sales agent to verify property_identifiers: List of identifiers to match property_type: Type of property (website, app, etc.) - optional timeout: Request timeout in seconds client: Optional httpx.AsyncClient for connection pooling Returns: True if agent is authorized, False otherwise Raises: AdagentsNotFoundError: If adagents.json not found (404) AdagentsValidationError: If JSON is invalid or malformed AdagentsTimeoutError: If request times out """ adagents_data = await fetch_adagents(publisher_domain, timeout=timeout, client=client) return verify_agent_authorization( adagents_data=adagents_data, agent_url=agent_url, property_type=property_type, property_identifiers=property_identifiers, )Convenience wrapper to fetch adagents.json and verify authorization in one call.
Args
publisher_domain- Domain hosting the adagents.json file
agent_url- URL of the sales agent to verify
property_identifiers- List of identifiers to match
property_type- Type of property (website, app, etc.) - optional
timeout- Request timeout in seconds
client- Optional httpx.AsyncClient for connection pooling
Returns
True if agent is authorized, False otherwise
Raises
AdagentsNotFoundError- If adagents.json not found (404)
AdagentsValidationError- If JSON is invalid or malformed
AdagentsTimeoutError- If request times out
Classes
class ADCPAuthenticationError (message: str, agent_id: str | None = None, agent_uri: str | None = None)-
Expand source code
class ADCPAuthenticationError(ADCPError): """Authentication failed (401, 403).""" def __init__(self, message: str, agent_id: str | None = None, agent_uri: str | None = None): """Initialize authentication error.""" suggestion = ( "Check that your auth_token is valid and not expired.\n" " Verify auth_type ('bearer' vs 'token') and auth_header are correct.\n" " Some agents (like Optable) require auth_type='bearer' and " "auth_header='Authorization'" ) super().__init__(message, agent_id, agent_uri, suggestion)Authentication failed (401, 403).
Initialize authentication error.
Ancestors
- ADCPError
- builtins.Exception
- builtins.BaseException
class ADCPClient (agent_config: AgentConfig,
webhook_url_template: str | None = None,
webhook_secret: str | None = None,
on_activity: Callable[[Activity], None] | None = None)-
Expand source code
class ADCPClient: """Client for interacting with a single AdCP agent.""" def __init__( self, agent_config: AgentConfig, webhook_url_template: str | None = None, webhook_secret: str | None = None, on_activity: Callable[[Activity], None] | None = None, ): """ Initialize ADCP client for a single agent. Args: agent_config: Agent configuration webhook_url_template: Template for webhook URLs with {agent_id}, {task_type}, {operation_id} webhook_secret: Secret for webhook signature verification on_activity: Callback for activity events """ self.agent_config = agent_config self.webhook_url_template = webhook_url_template self.webhook_secret = webhook_secret self.on_activity = on_activity # Initialize protocol adapter self.adapter: ProtocolAdapter if agent_config.protocol == Protocol.A2A: self.adapter = A2AAdapter(agent_config) elif agent_config.protocol == Protocol.MCP: self.adapter = MCPAdapter(agent_config) else: raise ValueError(f"Unsupported protocol: {agent_config.protocol}") # Initialize simple API accessor (lazy import to avoid circular dependency) from adcp.simple import SimpleAPI self.simple = SimpleAPI(self) def get_webhook_url(self, task_type: str, operation_id: str) -> str: """Generate webhook URL for a task.""" if not self.webhook_url_template: raise ValueError("webhook_url_template not configured") return self.webhook_url_template.format( agent_id=self.agent_config.id, task_type=task_type, operation_id=operation_id, ) def _emit_activity(self, activity: Activity) -> None: """Emit activity event.""" if self.on_activity: self.on_activity(activity) async def get_products( self, request: GetProductsRequest, fetch_previews: bool = False, preview_output_format: str = "url", creative_agent_client: ADCPClient | None = None, ) -> TaskResult[GetProductsResponse]: """ Get advertising products. Args: request: Request parameters fetch_previews: If True, generate preview URLs for each product's formats (uses batch API for 5-10x performance improvement) preview_output_format: "url" for iframe URLs (default), "html" for direct embedding (2-3x faster, no iframe overhead) creative_agent_client: Client for creative agent (required if fetch_previews=True) Returns: TaskResult containing GetProductsResponse with optional preview URLs in metadata Raises: ValueError: If fetch_previews=True but creative_agent_client is not provided """ if fetch_previews and not creative_agent_client: raise ValueError("creative_agent_client is required when fetch_previews=True") operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_products", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_products(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_products", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) result: TaskResult[GetProductsResponse] = self.adapter._parse_response( raw_result, GetProductsResponse ) if fetch_previews and result.success and result.data and creative_agent_client: from adcp.utils.preview_cache import add_preview_urls_to_products products_with_previews = await add_preview_urls_to_products( result.data.products, creative_agent_client, use_batch=True, output_format=preview_output_format, ) result.metadata = result.metadata or {} result.metadata["products_with_previews"] = products_with_previews return result async def list_creative_formats( self, request: ListCreativeFormatsRequest, fetch_previews: bool = False, preview_output_format: str = "url", ) -> TaskResult[ListCreativeFormatsResponse]: """ List supported creative formats. Args: request: Request parameters fetch_previews: If True, generate preview URLs for each format using sample manifests (uses batch API for 5-10x performance improvement) preview_output_format: "url" for iframe URLs (default), "html" for direct embedding (2-3x faster, no iframe overhead) Returns: TaskResult containing ListCreativeFormatsResponse with optional preview URLs in metadata """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_creative_formats", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.list_creative_formats(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_creative_formats", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) result: TaskResult[ListCreativeFormatsResponse] = self.adapter._parse_response( raw_result, ListCreativeFormatsResponse ) if fetch_previews and result.success and result.data: from adcp.utils.preview_cache import add_preview_urls_to_formats formats_with_previews = await add_preview_urls_to_formats( result.data.formats, self, use_batch=True, output_format=preview_output_format, ) result.metadata = result.metadata or {} result.metadata["formats_with_previews"] = formats_with_previews return result async def preview_creative( self, request: PreviewCreativeRequest, ) -> TaskResult[PreviewCreativeResponse]: """ Generate preview of a creative manifest. Args: request: Request parameters Returns: TaskResult containing PreviewCreativeResponse with preview URLs """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="preview_creative", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.preview_creative(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="preview_creative", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, PreviewCreativeResponse) async def sync_creatives( self, request: SyncCreativesRequest, ) -> TaskResult[SyncCreativesResponse]: """ Sync Creatives. Args: request: Request parameters Returns: TaskResult containing SyncCreativesResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_creatives", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.sync_creatives(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_creatives", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SyncCreativesResponse) async def list_creatives( self, request: ListCreativesRequest, ) -> TaskResult[ListCreativesResponse]: """ List Creatives. Args: request: Request parameters Returns: TaskResult containing ListCreativesResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_creatives", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.list_creatives(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_creatives", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ListCreativesResponse) async def get_media_buy_delivery( self, request: GetMediaBuyDeliveryRequest, ) -> TaskResult[GetMediaBuyDeliveryResponse]: """ Get Media Buy Delivery. Args: request: Request parameters Returns: TaskResult containing GetMediaBuyDeliveryResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_media_buy_delivery", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_media_buy_delivery(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_media_buy_delivery", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetMediaBuyDeliveryResponse) async def get_signals( self, request: GetSignalsRequest, ) -> TaskResult[GetSignalsResponse]: """ Get Signals. Args: request: Request parameters Returns: TaskResult containing GetSignalsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_signals", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_signals(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_signals", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetSignalsResponse) async def activate_signal( self, request: ActivateSignalRequest, ) -> TaskResult[ActivateSignalResponse]: """ Activate Signal. Args: request: Request parameters Returns: TaskResult containing ActivateSignalResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="activate_signal", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.activate_signal(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="activate_signal", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ActivateSignalResponse) async def provide_performance_feedback( self, request: ProvidePerformanceFeedbackRequest, ) -> TaskResult[ProvidePerformanceFeedbackResponse]: """ Provide Performance Feedback. Args: request: Request parameters Returns: TaskResult containing ProvidePerformanceFeedbackResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="provide_performance_feedback", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.provide_performance_feedback(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="provide_performance_feedback", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ProvidePerformanceFeedbackResponse) async def create_media_buy( self, request: CreateMediaBuyRequest, ) -> TaskResult[CreateMediaBuyResponse]: """ Create a new media buy reservation. Requests the agent to reserve inventory for a campaign. The agent returns a media_buy_id that tracks this reservation and can be used for updates. Args: request: Media buy creation parameters including: - brand_manifest: Advertiser brand information and creative assets - packages: List of package requests specifying desired inventory - publisher_properties: Target properties for ad placement - budget: Optional budget constraints - start_date/end_date: Campaign flight dates Returns: TaskResult containing CreateMediaBuyResponse with: - media_buy_id: Unique identifier for this reservation - status: Current state of the media buy - packages: Confirmed package details - Additional platform-specific metadata Example: >>> from adcp import ADCPClient, CreateMediaBuyRequest >>> client = ADCPClient(agent_config) >>> request = CreateMediaBuyRequest( ... brand_manifest=brand, ... packages=[package_request], ... publisher_properties=properties ... ) >>> result = await client.create_media_buy(request) >>> if result.success: ... media_buy_id = result.data.media_buy_id """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_media_buy", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.create_media_buy(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_media_buy", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, CreateMediaBuyResponse) async def update_media_buy( self, request: UpdateMediaBuyRequest, ) -> TaskResult[UpdateMediaBuyResponse]: """ Update an existing media buy reservation. Modifies a previously created media buy by updating packages or publisher properties. The update operation uses discriminated unions to specify what to change - either package details or targeting properties. Args: request: Media buy update parameters including: - media_buy_id: Identifier from create_media_buy response - updates: Discriminated union specifying update type: * UpdateMediaBuyPackagesRequest: Modify package selections * UpdateMediaBuyPropertiesRequest: Change targeting properties Returns: TaskResult containing UpdateMediaBuyResponse with: - media_buy_id: The updated media buy identifier - status: Updated state of the media buy - packages: Updated package configurations - Additional platform-specific metadata Example: >>> from adcp import ADCPClient, UpdateMediaBuyPackagesRequest >>> client = ADCPClient(agent_config) >>> request = UpdateMediaBuyPackagesRequest( ... media_buy_id="mb_123", ... packages=[updated_package] ... ) >>> result = await client.update_media_buy(request) >>> if result.success: ... updated_packages = result.data.packages """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_media_buy", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.update_media_buy(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_media_buy", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, UpdateMediaBuyResponse) async def build_creative( self, request: BuildCreativeRequest, ) -> TaskResult[BuildCreativeResponse]: """ Generate production-ready creative assets. Requests the creative agent to build final deliverable assets in the target format (e.g., VAST, DAAST, HTML5). This is typically called after previewing and approving a creative manifest. Args: request: Creative build parameters including: - manifest: Creative manifest with brand info and content - target_format_id: Desired output format identifier - inputs: Optional user-provided inputs for template variables - deployment: Platform or agent deployment configuration Returns: TaskResult containing BuildCreativeResponse with: - assets: Production-ready creative files (URLs or inline content) - format_id: The generated format identifier - manifest: The creative manifest used for generation - metadata: Additional platform-specific details Example: >>> from adcp import ADCPClient, BuildCreativeRequest >>> client = ADCPClient(agent_config) >>> request = BuildCreativeRequest( ... manifest=creative_manifest, ... target_format_id="vast_2.0", ... inputs={"duration": 30} ... ) >>> result = await client.build_creative(request) >>> if result.success: ... vast_url = result.data.assets[0].url """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="build_creative", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.build_creative(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="build_creative", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, BuildCreativeResponse) async def list_accounts( self, request: ListAccountsRequest, ) -> TaskResult[ListAccountsResponse]: """ List Accounts. Args: request: Request parameters Returns: TaskResult containing ListAccountsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_accounts", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.list_accounts(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_accounts", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ListAccountsResponse) async def sync_accounts( self, request: SyncAccountsRequest, ) -> TaskResult[SyncAccountsResponse]: """ Sync Accounts. Args: request: Request parameters Returns: TaskResult containing SyncAccountsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_accounts", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.sync_accounts(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_accounts", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SyncAccountsResponse) async def log_event( self, request: LogEventRequest, ) -> TaskResult[LogEventResponse]: """ Log Event. Args: request: Request parameters Returns: TaskResult containing LogEventResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="log_event", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.log_event(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="log_event", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, LogEventResponse) async def sync_event_sources( self, request: SyncEventSourcesRequest, ) -> TaskResult[SyncEventSourcesResponse]: """ Sync Event Sources. Args: request: Request parameters Returns: TaskResult containing SyncEventSourcesResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_event_sources", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.sync_event_sources(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_event_sources", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SyncEventSourcesResponse) async def get_creative_delivery( self, request: GetCreativeDeliveryRequest, ) -> TaskResult[GetCreativeDeliveryResponse]: """ Get Creative Delivery. Args: request: Request parameters Returns: TaskResult containing GetCreativeDeliveryResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_creative_delivery", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_creative_delivery(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_creative_delivery", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetCreativeDeliveryResponse) # ======================================================================== # V3 Protocol Methods - Protocol Discovery # ======================================================================== async def get_adcp_capabilities( self, request: GetAdcpCapabilitiesRequest, ) -> TaskResult[GetAdcpCapabilitiesResponse]: """ Get AdCP capabilities from the agent. Queries the agent's supported AdCP features, protocol versions, and domain-specific capabilities (media_buy, signals, sponsored_intelligence). Args: request: Request parameters including optional protocol filters Returns: TaskResult containing GetAdcpCapabilitiesResponse with: - adcp: Core protocol version information - supported_protocols: List of supported domain protocols - media_buy: Media buy capabilities (if supported) - sponsored_intelligence: SI capabilities (if supported) - signals: Signals capabilities (if supported) """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_adcp_capabilities", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_adcp_capabilities(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_adcp_capabilities", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetAdcpCapabilitiesResponse) # ======================================================================== # V3 Protocol Methods - Content Standards # ======================================================================== async def create_content_standards( self, request: CreateContentStandardsRequest, ) -> TaskResult[CreateContentStandardsResponse]: """ Create a new content standards configuration. Defines acceptable content contexts for ad placement using natural language policy and optional calibration exemplars. Args: request: Request parameters including policy and scope Returns: TaskResult containing CreateContentStandardsResponse with standards_id """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_content_standards", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.create_content_standards(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_content_standards", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, CreateContentStandardsResponse) async def get_content_standards( self, request: GetContentStandardsRequest, ) -> TaskResult[GetContentStandardsResponse]: """ Get a content standards configuration by ID. Args: request: Request parameters including standards_id Returns: TaskResult containing GetContentStandardsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_content_standards", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_content_standards(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_content_standards", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetContentStandardsResponse) async def list_content_standards( self, request: ListContentStandardsRequest, ) -> TaskResult[ListContentStandardsResponse]: """ List content standards configurations. Args: request: Request parameters including optional filters Returns: TaskResult containing ListContentStandardsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_content_standards", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.list_content_standards(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_content_standards", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ListContentStandardsResponse) async def update_content_standards( self, request: UpdateContentStandardsRequest, ) -> TaskResult[UpdateContentStandardsResponse]: """ Update a content standards configuration. Args: request: Request parameters including standards_id and updates Returns: TaskResult containing UpdateContentStandardsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_content_standards", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.update_content_standards(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_content_standards", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, UpdateContentStandardsResponse) async def calibrate_content( self, request: CalibrateContentRequest, ) -> TaskResult[CalibrateContentResponse]: """ Calibrate content against standards. Evaluates content (artifact or URL) against configured standards to determine suitability for ad placement. Args: request: Request parameters including content to evaluate Returns: TaskResult containing CalibrateContentResponse with verdict """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="calibrate_content", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.calibrate_content(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="calibrate_content", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, CalibrateContentResponse) async def validate_content_delivery( self, request: ValidateContentDeliveryRequest, ) -> TaskResult[ValidateContentDeliveryResponse]: """ Validate content delivery against standards. Validates that ad delivery records comply with content standards. Args: request: Request parameters including delivery records Returns: TaskResult containing ValidateContentDeliveryResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="validate_content_delivery", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.validate_content_delivery(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="validate_content_delivery", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ValidateContentDeliveryResponse) async def get_media_buy_artifacts( self, request: GetMediaBuyArtifactsRequest, ) -> TaskResult[GetMediaBuyArtifactsResponse]: """ Get artifacts associated with a media buy. Retrieves content artifacts where ads were delivered for a media buy. Args: request: Request parameters including media_buy_id Returns: TaskResult containing GetMediaBuyArtifactsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_media_buy_artifacts", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_media_buy_artifacts(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_media_buy_artifacts", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetMediaBuyArtifactsResponse) # ======================================================================== # V3 Protocol Methods - Sponsored Intelligence # ======================================================================== async def si_get_offering( self, request: SiGetOfferingRequest, ) -> TaskResult[SiGetOfferingResponse]: """ Get sponsored intelligence offering. Retrieves product/service offerings that can be presented in a sponsored intelligence session. Args: request: Request parameters including brand context Returns: TaskResult containing SiGetOfferingResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_get_offering", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.si_get_offering(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_get_offering", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SiGetOfferingResponse) async def si_initiate_session( self, request: SiInitiateSessionRequest, ) -> TaskResult[SiInitiateSessionResponse]: """ Initiate a sponsored intelligence session. Starts a conversational brand experience session with a user. Args: request: Request parameters including identity and context Returns: TaskResult containing SiInitiateSessionResponse with session_id """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_initiate_session", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.si_initiate_session(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_initiate_session", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SiInitiateSessionResponse) async def si_send_message( self, request: SiSendMessageRequest, ) -> TaskResult[SiSendMessageResponse]: """ Send a message in a sponsored intelligence session. Continues the conversation in an active SI session. Args: request: Request parameters including session_id and message Returns: TaskResult containing SiSendMessageResponse with brand response """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_send_message", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.si_send_message(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_send_message", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SiSendMessageResponse) async def si_terminate_session( self, request: SiTerminateSessionRequest, ) -> TaskResult[SiTerminateSessionResponse]: """ Terminate a sponsored intelligence session. Ends an active SI session, optionally with follow-up actions. Args: request: Request parameters including session_id and termination context Returns: TaskResult containing SiTerminateSessionResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_terminate_session", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.si_terminate_session(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_terminate_session", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SiTerminateSessionResponse) # ======================================================================== # V3 Governance (Property Lists) Methods # ======================================================================== async def create_property_list( self, request: CreatePropertyListRequest, ) -> TaskResult[CreatePropertyListResponse]: """ Create a property list for governance filtering. Property lists define dynamic sets of properties based on filters, brand manifests, and feature requirements. Args: request: Request parameters for creating the property list Returns: TaskResult containing CreatePropertyListResponse with list_id """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_property_list", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.create_property_list(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_property_list", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, CreatePropertyListResponse) async def get_property_list( self, request: GetPropertyListRequest, ) -> TaskResult[GetPropertyListResponse]: """ Get a property list with optional resolution. When resolve=true, returns the list of resolved property identifiers. Use this to get the actual properties that match the list's filters. Args: request: Request parameters including list_id and resolve flag Returns: TaskResult containing GetPropertyListResponse with identifiers """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_property_list", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_property_list(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_property_list", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetPropertyListResponse) async def list_property_lists( self, request: ListPropertyListsRequest, ) -> TaskResult[ListPropertyListsResponse]: """ List property lists owned by a principal. Retrieves metadata for all property lists, optionally filtered by principal or pagination parameters. Args: request: Request parameters with optional filtering Returns: TaskResult containing ListPropertyListsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_property_lists", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.list_property_lists(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_property_lists", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ListPropertyListsResponse) async def update_property_list( self, request: UpdatePropertyListRequest, ) -> TaskResult[UpdatePropertyListResponse]: """ Update a property list. Modifies the filters, brand manifest, or other parameters of an existing property list. Args: request: Request parameters with list_id and updates Returns: TaskResult containing UpdatePropertyListResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_property_list", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.update_property_list(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_property_list", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, UpdatePropertyListResponse) async def delete_property_list( self, request: DeletePropertyListRequest, ) -> TaskResult[DeletePropertyListResponse]: """ Delete a property list. Removes a property list. Any active subscriptions to this list will be terminated. Args: request: Request parameters with list_id Returns: TaskResult containing DeletePropertyListResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="delete_property_list", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.delete_property_list(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="delete_property_list", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, DeletePropertyListResponse) async def list_tools(self) -> list[str]: """ List available tools from the agent. Returns: List of tool names """ return await self.adapter.list_tools() async def get_info(self) -> dict[str, Any]: """ Get agent information including AdCP extension metadata. Returns agent card information including: - Agent name, description, version - Protocol type (mcp or a2a) - AdCP version (from extensions.adcp.adcp_version) - Supported protocols (from extensions.adcp.protocols_supported) - Available tools/skills Returns: Dictionary with agent metadata """ return await self.adapter.get_agent_info() async def close(self) -> None: """Close the adapter and clean up resources.""" if hasattr(self.adapter, "close"): logger.debug(f"Closing adapter for agent {self.agent_config.id}") await self.adapter.close() async def __aenter__(self) -> ADCPClient: """Async context manager entry.""" return self async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: """Async context manager exit.""" await self.close() def _verify_webhook_signature( self, payload: dict[str, Any], signature: str, timestamp: str ) -> bool: """ Verify HMAC-SHA256 signature of webhook payload. The verification algorithm matches get_adcp_signed_headers_for_webhook: 1. Constructs message as "{timestamp}.{json_payload}" 2. JSON-serializes payload with compact separators 3. UTF-8 encodes the message 4. HMAC-SHA256 signs with the shared secret 5. Compares against the provided signature (with "sha256=" prefix stripped) Args: payload: Webhook payload dict signature: Signature to verify (with or without "sha256=" prefix) timestamp: ISO 8601 timestamp from X-AdCP-Timestamp header Returns: True if signature is valid, False otherwise """ if not self.webhook_secret: return True # Strip "sha256=" prefix if present if signature.startswith("sha256="): signature = signature[7:] # Serialize payload to JSON with consistent formatting (matches signing) payload_bytes = json.dumps(payload, separators=(",", ":"), sort_keys=False).encode("utf-8") # Construct signed message: timestamp.payload (matches get_adcp_signed_headers_for_webhook) signed_message = f"{timestamp}.{payload_bytes.decode('utf-8')}" # Generate expected signature expected_signature = hmac.new( self.webhook_secret.encode("utf-8"), signed_message.encode("utf-8"), hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected_signature) def _parse_webhook_result( self, task_id: str, task_type: str, operation_id: str, status: GeneratedTaskStatus, result: Any, timestamp: datetime | str, message: str | None, context_id: str | None, ) -> TaskResult[AdcpAsyncResponseData]: """ Parse webhook data into typed TaskResult based on task_type. Args: task_id: Unique identifier for this task task_type: Task type from application routing (e.g., "get_products") operation_id: Operation identifier from application routing status: Current task status result: Task-specific payload (AdCP response data) timestamp: ISO 8601 timestamp when webhook was generated message: Human-readable summary of task state context_id: Session/conversation identifier Returns: TaskResult with task-specific typed response data Note: This method works with both MCP and A2A protocols by accepting protocol-agnostic parameters rather than protocol-specific objects. """ from adcp.utils.response_parser import parse_json_or_text # Map task types to their response types (using string literals, not enum) # Note: Some response types are Union types (e.g., ActivateSignalResponse = Success | Error) response_type_map: dict[str, type[BaseModel] | Any] = { # Core operations "get_products": GetProductsResponse, "list_creative_formats": ListCreativeFormatsResponse, "sync_creatives": SyncCreativesResponse, "list_creatives": ListCreativesResponse, "build_creative": BuildCreativeResponse, "preview_creative": PreviewCreativeResponse, "create_media_buy": CreateMediaBuyResponse, "update_media_buy": UpdateMediaBuyResponse, "get_media_buy_delivery": GetMediaBuyDeliveryResponse, "get_signals": GetSignalsResponse, "activate_signal": ActivateSignalResponse, "provide_performance_feedback": ProvidePerformanceFeedbackResponse, "list_accounts": ListAccountsResponse, "sync_accounts": SyncAccountsResponse, "log_event": LogEventResponse, "sync_event_sources": SyncEventSourcesResponse, "get_creative_delivery": GetCreativeDeliveryResponse, # V3 Protocol Discovery "get_adcp_capabilities": GetAdcpCapabilitiesResponse, # V3 Content Standards "create_content_standards": CreateContentStandardsResponse, "get_content_standards": GetContentStandardsResponse, "list_content_standards": ListContentStandardsResponse, "update_content_standards": UpdateContentStandardsResponse, "calibrate_content": CalibrateContentResponse, "validate_content_delivery": ValidateContentDeliveryResponse, "get_media_buy_artifacts": GetMediaBuyArtifactsResponse, # V3 Sponsored Intelligence "si_get_offering": SiGetOfferingResponse, "si_initiate_session": SiInitiateSessionResponse, "si_send_message": SiSendMessageResponse, "si_terminate_session": SiTerminateSessionResponse, # V3 Governance (Property Lists) "create_property_list": CreatePropertyListResponse, "get_property_list": GetPropertyListResponse, "list_property_lists": ListPropertyListsResponse, "update_property_list": UpdatePropertyListResponse, "delete_property_list": DeletePropertyListResponse, } # Handle completed tasks with result parsing if status == GeneratedTaskStatus.completed and result is not None: response_type = response_type_map.get(task_type) if response_type: try: parsed_result: Any = parse_json_or_text(result, response_type) return TaskResult[AdcpAsyncResponseData]( status=TaskStatus.COMPLETED, data=parsed_result, success=True, metadata={ "task_id": task_id, "operation_id": operation_id, "timestamp": timestamp, "message": message, }, ) except ValueError as e: logger.warning(f"Failed to parse webhook result: {e}") # Fall through to untyped result # Handle failed, input-required, or unparseable results # Convert status to core TaskStatus enum status_map = { GeneratedTaskStatus.completed: TaskStatus.COMPLETED, GeneratedTaskStatus.submitted: TaskStatus.SUBMITTED, GeneratedTaskStatus.working: TaskStatus.WORKING, GeneratedTaskStatus.failed: TaskStatus.FAILED, GeneratedTaskStatus.input_required: TaskStatus.NEEDS_INPUT, } task_status = status_map.get(status, TaskStatus.FAILED) # Extract error message from result.errors if present error_message: str | None = None if result is not None and hasattr(result, "errors"): errors = getattr(result, "errors", None) if errors and len(errors) > 0: first_error = errors[0] if hasattr(first_error, "message"): error_message = first_error.message return TaskResult[AdcpAsyncResponseData]( status=task_status, data=result, success=status == GeneratedTaskStatus.completed, error=error_message, metadata={ "task_id": task_id, "operation_id": operation_id, "timestamp": timestamp, "message": message, "context_id": context_id, }, ) async def _handle_mcp_webhook( self, payload: dict[str, Any], task_type: str, operation_id: str, signature: str | None, timestamp: str | None = None, ) -> TaskResult[AdcpAsyncResponseData]: """ Handle MCP webhook delivered via HTTP POST. Args: payload: Webhook payload dict task_type: Task type from application routing operation_id: Operation identifier from application routing signature: Optional HMAC-SHA256 signature for verification (X-AdCP-Signature header) timestamp: Optional timestamp for signature verification (X-AdCP-Timestamp header) Returns: TaskResult with parsed task-specific response data Raises: ADCPWebhookSignatureError: If signature verification fails ValidationError: If payload doesn't match McpWebhookPayload schema """ from adcp.types.generated_poc.core.mcp_webhook_payload import McpWebhookPayload # Verify signature before processing (requires both signature and timestamp) if ( signature and timestamp and not self._verify_webhook_signature(payload, signature, timestamp) ): logger.warning( f"Webhook signature verification failed for agent {self.agent_config.id}" ) raise ADCPWebhookSignatureError("Invalid webhook signature") # Validate and parse MCP webhook payload webhook = McpWebhookPayload.model_validate(payload) # Emit activity for monitoring self._emit_activity( Activity( type=ActivityType.WEBHOOK_RECEIVED, operation_id=operation_id, agent_id=self.agent_config.id, task_type=task_type, timestamp=datetime.now(timezone.utc).isoformat(), metadata={"payload": payload, "protocol": "mcp"}, ) ) # Extract fields and parse result return self._parse_webhook_result( task_id=webhook.task_id, task_type=task_type, operation_id=operation_id, status=webhook.status, result=webhook.result, timestamp=webhook.timestamp, message=webhook.message, context_id=webhook.context_id, ) async def _handle_a2a_webhook( self, payload: Task | TaskStatusUpdateEvent, task_type: str, operation_id: str ) -> TaskResult[AdcpAsyncResponseData]: """ Handle A2A webhook delivered through Task or TaskStatusUpdateEvent. Per A2A specification: - Terminated statuses (completed, failed): Payload is Task with artifacts[].parts[] - Intermediate statuses (working, input-required, submitted): Payload is TaskStatusUpdateEvent with status.message.parts[] Args: payload: A2A Task or TaskStatusUpdateEvent object task_type: Task type from application routing operation_id: Operation identifier from application routing Returns: TaskResult with parsed task-specific response data Note: Signature verification is NOT applicable for A2A webhooks as they arrive through authenticated A2A connections, not HTTP. """ from a2a.types import DataPart, TextPart adcp_data: Any = None text_message: str | None = None task_id: str context_id: str | None status_state: str timestamp: datetime | str # Type detection and extraction based on payload type if isinstance(payload, TaskStatusUpdateEvent): # Intermediate status: Extract from status.message.parts[] task_id = payload.task_id context_id = payload.context_id status_state = payload.status.state if payload.status else "failed" timestamp = ( payload.status.timestamp if payload.status and payload.status.timestamp else datetime.now(timezone.utc) ) # Extract from status.message.parts[] if payload.status and payload.status.message and payload.status.message.parts: # Extract DataPart for structured AdCP payload data_parts = [ p.root for p in payload.status.message.parts if isinstance(p.root, DataPart) ] if data_parts: # Use last DataPart as authoritative last_data_part = data_parts[-1] adcp_data = last_data_part.data # Unwrap {"response": {...}} wrapper if present (ADK pattern) if isinstance(adcp_data, dict) and "response" in adcp_data: adcp_data = adcp_data["response"] # Extract TextPart for human-readable message for part in payload.status.message.parts: if isinstance(part.root, TextPart): text_message = part.root.text break else: # Terminated status (Task): Extract from artifacts[].parts[] task_id = payload.id context_id = payload.context_id status_state = payload.status.state if payload.status else "failed" timestamp = ( payload.status.timestamp if payload.status and payload.status.timestamp else datetime.now(timezone.utc) ) # Extract from task.artifacts[].parts[] # Following A2A spec: use last artifact, last DataPart is authoritative if payload.artifacts: # Use last artifact (most recent in streaming scenarios) target_artifact = payload.artifacts[-1] if target_artifact.parts: # Extract DataPart for structured AdCP payload data_parts = [ p.root for p in target_artifact.parts if isinstance(p.root, DataPart) ] if data_parts: # Use last DataPart as authoritative last_data_part = data_parts[-1] adcp_data = last_data_part.data # Unwrap {"response": {...}} wrapper if present (ADK pattern) if isinstance(adcp_data, dict) and "response" in adcp_data: adcp_data = adcp_data["response"] # Extract TextPart for human-readable message for part in target_artifact.parts: if isinstance(part.root, TextPart): text_message = part.root.text break # Map A2A status.state to GeneratedTaskStatus enum status_map = { "completed": GeneratedTaskStatus.completed, "submitted": GeneratedTaskStatus.submitted, "working": GeneratedTaskStatus.working, "failed": GeneratedTaskStatus.failed, "input-required": GeneratedTaskStatus.input_required, "input_required": GeneratedTaskStatus.input_required, # Handle both formats } mapped_status = status_map.get(status_state, GeneratedTaskStatus.failed) # Emit activity for monitoring self._emit_activity( Activity( type=ActivityType.WEBHOOK_RECEIVED, operation_id=operation_id, agent_id=self.agent_config.id, task_type=task_type, timestamp=datetime.now(timezone.utc).isoformat(), metadata={ "task_id": task_id, "protocol": "a2a", "payload_type": ( "TaskStatusUpdateEvent" if isinstance(payload, TaskStatusUpdateEvent) else "Task" ), }, ) ) # Parse and return typed result by passing extracted fields directly return self._parse_webhook_result( task_id=task_id, task_type=task_type, operation_id=operation_id, status=mapped_status, result=adcp_data, timestamp=timestamp, message=text_message, context_id=context_id, ) async def handle_webhook( self, payload: dict[str, Any] | Task | TaskStatusUpdateEvent, task_type: str, operation_id: str, signature: str | None = None, timestamp: str | None = None, ) -> TaskResult[AdcpAsyncResponseData]: """ Handle incoming webhook and return typed result. This method provides a unified interface for handling webhooks from both MCP and A2A protocols: - MCP Webhooks: HTTP POST with dict payload, optional HMAC signature - A2A Webhooks: Task or TaskStatusUpdateEvent objects based on status The method automatically detects the protocol type and routes to the appropriate handler. Both protocols return a consistent TaskResult structure with typed AdCP response data. Args: payload: Webhook payload - one of: - dict[str, Any]: MCP webhook payload from HTTP POST - Task: A2A webhook for terminated statuses (completed, failed) - TaskStatusUpdateEvent: A2A webhook for intermediate statuses (working, input-required, submitted) task_type: Task type from application routing (e.g., "get_products"). Applications should extract this from URL routing pattern: /webhook/{task_type}/{agent_id}/{operation_id} operation_id: Operation identifier from application routing. Used to correlate webhook notifications with original task submission. signature: Optional HMAC-SHA256 signature for MCP webhook verification (X-AdCP-Signature header). Ignored for A2A webhooks. timestamp: Optional timestamp for MCP webhook signature verification (X-AdCP-Timestamp header). Required when signature is provided. Returns: TaskResult with parsed task-specific response data. The structure is identical regardless of protocol. Raises: ADCPWebhookSignatureError: If MCP signature verification fails ValidationError: If MCP payload doesn't match WebhookPayload schema Note: task_type and operation_id were deprecated from the webhook payload per AdCP specification. Applications must extract these from URL routing and pass them explicitly. Examples: MCP webhook (HTTP endpoint): >>> @app.post("/webhook/{task_type}/{agent_id}/{operation_id}") >>> async def webhook_handler(task_type: str, operation_id: str, request: Request): >>> payload = await request.json() >>> signature = request.headers.get("X-AdCP-Signature") >>> timestamp = request.headers.get("X-AdCP-Timestamp") >>> result = await client.handle_webhook( >>> payload, task_type, operation_id, signature, timestamp >>> ) >>> if result.success: >>> print(f"Task completed: {result.data}") A2A webhook with Task (terminated status): >>> async def on_task_completed(task: Task): >>> # Extract task_type and operation_id from your app's task tracking >>> task_type = your_task_registry.get_type(task.id) >>> operation_id = your_task_registry.get_operation_id(task.id) >>> result = await client.handle_webhook( >>> task, task_type, operation_id >>> ) >>> if result.success: >>> print(f"Task completed: {result.data}") A2A webhook with TaskStatusUpdateEvent (intermediate status): >>> async def on_task_update(event: TaskStatusUpdateEvent): >>> # Extract task_type and operation_id from your app's task tracking >>> task_type = your_task_registry.get_type(event.task_id) >>> operation_id = your_task_registry.get_operation_id(event.task_id) >>> result = await client.handle_webhook( >>> event, task_type, operation_id >>> ) >>> if result.status == GeneratedTaskStatus.working: >>> print(f"Task still working: {result.metadata.get('message')}") """ # Detect protocol type and route to appropriate handler if isinstance(payload, (Task, TaskStatusUpdateEvent)): # A2A webhook (Task or TaskStatusUpdateEvent) return await self._handle_a2a_webhook(payload, task_type, operation_id) else: # MCP webhook (dict payload) return await self._handle_mcp_webhook( payload, task_type, operation_id, signature, timestamp )Client for interacting with a single AdCP agent.
Initialize ADCP client for a single agent.
Args
agent_config- Agent configuration
webhook_url_template- Template for webhook URLs with {agent_id}, {task_type}, {operation_id}
webhook_secret- Secret for webhook signature verification
on_activity- Callback for activity events
Methods
async def activate_signal(self,
request: ActivateSignalRequest) ‑> TaskResult[ActivateSignalResponse]-
Expand source code
async def activate_signal( self, request: ActivateSignalRequest, ) -> TaskResult[ActivateSignalResponse]: """ Activate Signal. Args: request: Request parameters Returns: TaskResult containing ActivateSignalResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="activate_signal", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.activate_signal(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="activate_signal", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ActivateSignalResponse)Activate Signal.
Args
request- Request parameters
Returns
TaskResult containing ActivateSignalResponse
async def build_creative(self,
request: BuildCreativeRequest) ‑> TaskResult[BuildCreativeResponse]-
Expand source code
async def build_creative( self, request: BuildCreativeRequest, ) -> TaskResult[BuildCreativeResponse]: """ Generate production-ready creative assets. Requests the creative agent to build final deliverable assets in the target format (e.g., VAST, DAAST, HTML5). This is typically called after previewing and approving a creative manifest. Args: request: Creative build parameters including: - manifest: Creative manifest with brand info and content - target_format_id: Desired output format identifier - inputs: Optional user-provided inputs for template variables - deployment: Platform or agent deployment configuration Returns: TaskResult containing BuildCreativeResponse with: - assets: Production-ready creative files (URLs or inline content) - format_id: The generated format identifier - manifest: The creative manifest used for generation - metadata: Additional platform-specific details Example: >>> from adcp import ADCPClient, BuildCreativeRequest >>> client = ADCPClient(agent_config) >>> request = BuildCreativeRequest( ... manifest=creative_manifest, ... target_format_id="vast_2.0", ... inputs={"duration": 30} ... ) >>> result = await client.build_creative(request) >>> if result.success: ... vast_url = result.data.assets[0].url """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="build_creative", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.build_creative(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="build_creative", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, BuildCreativeResponse)Generate production-ready creative assets.
Requests the creative agent to build final deliverable assets in the target format (e.g., VAST, DAAST, HTML5). This is typically called after previewing and approving a creative manifest.
Args
request- Creative build parameters including: - manifest: Creative manifest with brand info and content - target_format_id: Desired output format identifier - inputs: Optional user-provided inputs for template variables - deployment: Platform or agent deployment configuration
Returns
TaskResult containing BuildCreativeResponse with: - assets: Production-ready creative files (URLs or inline content) - format_id: The generated format identifier - manifest: The creative manifest used for generation - metadata: Additional platform-specific details
Example
>>> from adcp import ADCPClient, BuildCreativeRequest >>> client = ADCPClient(agent_config) >>> request = BuildCreativeRequest( ... manifest=creative_manifest, ... target_format_id="vast_2.0", ... inputs={"duration": 30} ... ) >>> result = await client.build_creative(request) >>> if result.success: ... vast_url = result.data.assets[0].url async def calibrate_content(self, request: CalibrateContentRequest) ‑> TaskResult[CalibrateContentResponse]-
Expand source code
async def calibrate_content( self, request: CalibrateContentRequest, ) -> TaskResult[CalibrateContentResponse]: """ Calibrate content against standards. Evaluates content (artifact or URL) against configured standards to determine suitability for ad placement. Args: request: Request parameters including content to evaluate Returns: TaskResult containing CalibrateContentResponse with verdict """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="calibrate_content", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.calibrate_content(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="calibrate_content", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, CalibrateContentResponse)Calibrate content against standards.
Evaluates content (artifact or URL) against configured standards to determine suitability for ad placement.
Args
request- Request parameters including content to evaluate
Returns
TaskResult containing CalibrateContentResponse with verdict
async def close(self) ‑> None-
Expand source code
async def close(self) -> None: """Close the adapter and clean up resources.""" if hasattr(self.adapter, "close"): logger.debug(f"Closing adapter for agent {self.agent_config.id}") await self.adapter.close()Close the adapter and clean up resources.
async def create_content_standards(self, request: CreateContentStandardsRequest) ‑> TaskResult[CreateContentStandardsResponse]-
Expand source code
async def create_content_standards( self, request: CreateContentStandardsRequest, ) -> TaskResult[CreateContentStandardsResponse]: """ Create a new content standards configuration. Defines acceptable content contexts for ad placement using natural language policy and optional calibration exemplars. Args: request: Request parameters including policy and scope Returns: TaskResult containing CreateContentStandardsResponse with standards_id """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_content_standards", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.create_content_standards(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_content_standards", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, CreateContentStandardsResponse)Create a new content standards configuration.
Defines acceptable content contexts for ad placement using natural language policy and optional calibration exemplars.
Args
request- Request parameters including policy and scope
Returns
TaskResult containing CreateContentStandardsResponse with standards_id
async def create_media_buy(self,
request: CreateMediaBuyRequest) ‑> TaskResult[CreateMediaBuyResponse]-
Expand source code
async def create_media_buy( self, request: CreateMediaBuyRequest, ) -> TaskResult[CreateMediaBuyResponse]: """ Create a new media buy reservation. Requests the agent to reserve inventory for a campaign. The agent returns a media_buy_id that tracks this reservation and can be used for updates. Args: request: Media buy creation parameters including: - brand_manifest: Advertiser brand information and creative assets - packages: List of package requests specifying desired inventory - publisher_properties: Target properties for ad placement - budget: Optional budget constraints - start_date/end_date: Campaign flight dates Returns: TaskResult containing CreateMediaBuyResponse with: - media_buy_id: Unique identifier for this reservation - status: Current state of the media buy - packages: Confirmed package details - Additional platform-specific metadata Example: >>> from adcp import ADCPClient, CreateMediaBuyRequest >>> client = ADCPClient(agent_config) >>> request = CreateMediaBuyRequest( ... brand_manifest=brand, ... packages=[package_request], ... publisher_properties=properties ... ) >>> result = await client.create_media_buy(request) >>> if result.success: ... media_buy_id = result.data.media_buy_id """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_media_buy", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.create_media_buy(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_media_buy", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, CreateMediaBuyResponse)Create a new media buy reservation.
Requests the agent to reserve inventory for a campaign. The agent returns a media_buy_id that tracks this reservation and can be used for updates.
Args
request- Media buy creation parameters including: - brand_manifest: Advertiser brand information and creative assets - packages: List of package requests specifying desired inventory - publisher_properties: Target properties for ad placement - budget: Optional budget constraints - start_date/end_date: Campaign flight dates
Returns
TaskResult containing CreateMediaBuyResponse with: - media_buy_id: Unique identifier for this reservation - status: Current state of the media buy - packages: Confirmed package details - Additional platform-specific metadata
Example
>>> from adcp import ADCPClient, CreateMediaBuyRequest >>> client = ADCPClient(agent_config) >>> request = CreateMediaBuyRequest( ... brand_manifest=brand, ... packages=[package_request], ... publisher_properties=properties ... ) >>> result = await client.create_media_buy(request) >>> if result.success: ... media_buy_id = result.data.media_buy_id async def create_property_list(self, request: CreatePropertyListRequest) ‑> TaskResult[CreatePropertyListResponse]-
Expand source code
async def create_property_list( self, request: CreatePropertyListRequest, ) -> TaskResult[CreatePropertyListResponse]: """ Create a property list for governance filtering. Property lists define dynamic sets of properties based on filters, brand manifests, and feature requirements. Args: request: Request parameters for creating the property list Returns: TaskResult containing CreatePropertyListResponse with list_id """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_property_list", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.create_property_list(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="create_property_list", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, CreatePropertyListResponse)Create a property list for governance filtering.
Property lists define dynamic sets of properties based on filters, brand manifests, and feature requirements.
Args
request- Request parameters for creating the property list
Returns
TaskResult containing CreatePropertyListResponse with list_id
async def delete_property_list(self, request: DeletePropertyListRequest) ‑> TaskResult[DeletePropertyListResponse]-
Expand source code
async def delete_property_list( self, request: DeletePropertyListRequest, ) -> TaskResult[DeletePropertyListResponse]: """ Delete a property list. Removes a property list. Any active subscriptions to this list will be terminated. Args: request: Request parameters with list_id Returns: TaskResult containing DeletePropertyListResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="delete_property_list", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.delete_property_list(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="delete_property_list", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, DeletePropertyListResponse)Delete a property list.
Removes a property list. Any active subscriptions to this list will be terminated.
Args
request- Request parameters with list_id
Returns
TaskResult containing DeletePropertyListResponse
async def get_adcp_capabilities(self, request: GetAdcpCapabilitiesRequest) ‑> TaskResult[GetAdcpCapabilitiesResponse]-
Expand source code
async def get_adcp_capabilities( self, request: GetAdcpCapabilitiesRequest, ) -> TaskResult[GetAdcpCapabilitiesResponse]: """ Get AdCP capabilities from the agent. Queries the agent's supported AdCP features, protocol versions, and domain-specific capabilities (media_buy, signals, sponsored_intelligence). Args: request: Request parameters including optional protocol filters Returns: TaskResult containing GetAdcpCapabilitiesResponse with: - adcp: Core protocol version information - supported_protocols: List of supported domain protocols - media_buy: Media buy capabilities (if supported) - sponsored_intelligence: SI capabilities (if supported) - signals: Signals capabilities (if supported) """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_adcp_capabilities", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_adcp_capabilities(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_adcp_capabilities", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetAdcpCapabilitiesResponse)Get AdCP capabilities from the agent.
Queries the agent's supported AdCP features, protocol versions, and domain-specific capabilities (media_buy, signals, sponsored_intelligence).
Args
request- Request parameters including optional protocol filters
Returns
TaskResult containing GetAdcpCapabilitiesResponse with: - adcp: Core protocol version information - supported_protocols: List of supported domain protocols - media_buy: Media buy capabilities (if supported) - sponsored_intelligence: SI capabilities (if supported) - signals: Signals capabilities (if supported)
async def get_content_standards(self, request: GetContentStandardsRequest) ‑> TaskResult[GetContentStandardsResponse]-
Expand source code
async def get_content_standards( self, request: GetContentStandardsRequest, ) -> TaskResult[GetContentStandardsResponse]: """ Get a content standards configuration by ID. Args: request: Request parameters including standards_id Returns: TaskResult containing GetContentStandardsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_content_standards", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_content_standards(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_content_standards", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetContentStandardsResponse)Get a content standards configuration by ID.
Args
request- Request parameters including standards_id
Returns
TaskResult containing GetContentStandardsResponse
async def get_creative_delivery(self,
request: GetCreativeDeliveryRequest) ‑> TaskResult[GetCreativeDeliveryResponse]-
Expand source code
async def get_creative_delivery( self, request: GetCreativeDeliveryRequest, ) -> TaskResult[GetCreativeDeliveryResponse]: """ Get Creative Delivery. Args: request: Request parameters Returns: TaskResult containing GetCreativeDeliveryResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_creative_delivery", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_creative_delivery(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_creative_delivery", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetCreativeDeliveryResponse)Get Creative Delivery.
Args
request- Request parameters
Returns
TaskResult containing GetCreativeDeliveryResponse
async def get_info(self) ‑> dict[str, typing.Any]-
Expand source code
async def get_info(self) -> dict[str, Any]: """ Get agent information including AdCP extension metadata. Returns agent card information including: - Agent name, description, version - Protocol type (mcp or a2a) - AdCP version (from extensions.adcp.adcp_version) - Supported protocols (from extensions.adcp.protocols_supported) - Available tools/skills Returns: Dictionary with agent metadata """ return await self.adapter.get_agent_info()Get agent information including AdCP extension metadata.
Returns agent card information including: - Agent name, description, version - Protocol type (mcp or a2a) - AdCP version (from extensions.adcp.adcp_version) - Supported protocols (from extensions.adcp.protocols_supported) - Available tools/skills
Returns
Dictionary with agent metadata
async def get_media_buy_artifacts(self, request: GetMediaBuyArtifactsRequest) ‑> TaskResult[GetMediaBuyArtifactsResponse]-
Expand source code
async def get_media_buy_artifacts( self, request: GetMediaBuyArtifactsRequest, ) -> TaskResult[GetMediaBuyArtifactsResponse]: """ Get artifacts associated with a media buy. Retrieves content artifacts where ads were delivered for a media buy. Args: request: Request parameters including media_buy_id Returns: TaskResult containing GetMediaBuyArtifactsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_media_buy_artifacts", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_media_buy_artifacts(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_media_buy_artifacts", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetMediaBuyArtifactsResponse)Get artifacts associated with a media buy.
Retrieves content artifacts where ads were delivered for a media buy.
Args
request- Request parameters including media_buy_id
Returns
TaskResult containing GetMediaBuyArtifactsResponse
async def get_media_buy_delivery(self,
request: GetMediaBuyDeliveryRequest) ‑> TaskResult[GetMediaBuyDeliveryResponse]-
Expand source code
async def get_media_buy_delivery( self, request: GetMediaBuyDeliveryRequest, ) -> TaskResult[GetMediaBuyDeliveryResponse]: """ Get Media Buy Delivery. Args: request: Request parameters Returns: TaskResult containing GetMediaBuyDeliveryResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_media_buy_delivery", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_media_buy_delivery(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_media_buy_delivery", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetMediaBuyDeliveryResponse)Get Media Buy Delivery.
Args
request- Request parameters
Returns
TaskResult containing GetMediaBuyDeliveryResponse
async def get_products(self,
request: GetProductsRequest,
fetch_previews: bool = False,
preview_output_format: str = 'url',
creative_agent_client: ADCPClient | None = None) ‑> TaskResult[GetProductsResponse]-
Expand source code
async def get_products( self, request: GetProductsRequest, fetch_previews: bool = False, preview_output_format: str = "url", creative_agent_client: ADCPClient | None = None, ) -> TaskResult[GetProductsResponse]: """ Get advertising products. Args: request: Request parameters fetch_previews: If True, generate preview URLs for each product's formats (uses batch API for 5-10x performance improvement) preview_output_format: "url" for iframe URLs (default), "html" for direct embedding (2-3x faster, no iframe overhead) creative_agent_client: Client for creative agent (required if fetch_previews=True) Returns: TaskResult containing GetProductsResponse with optional preview URLs in metadata Raises: ValueError: If fetch_previews=True but creative_agent_client is not provided """ if fetch_previews and not creative_agent_client: raise ValueError("creative_agent_client is required when fetch_previews=True") operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_products", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_products(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_products", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) result: TaskResult[GetProductsResponse] = self.adapter._parse_response( raw_result, GetProductsResponse ) if fetch_previews and result.success and result.data and creative_agent_client: from adcp.utils.preview_cache import add_preview_urls_to_products products_with_previews = await add_preview_urls_to_products( result.data.products, creative_agent_client, use_batch=True, output_format=preview_output_format, ) result.metadata = result.metadata or {} result.metadata["products_with_previews"] = products_with_previews return resultGet advertising products.
Args
request- Request parameters
fetch_previews- If True, generate preview URLs for each product's formats (uses batch API for 5-10x performance improvement)
preview_output_format- "url" for iframe URLs (default), "html" for direct embedding (2-3x faster, no iframe overhead)
creative_agent_client- Client for creative agent (required if fetch_previews=True)
Returns
TaskResult containing GetProductsResponse with optional preview URLs in metadata
Raises
ValueError- If fetch_previews=True but creative_agent_client is not provided
async def get_property_list(self, request: GetPropertyListRequest) ‑> TaskResult[GetPropertyListResponse]-
Expand source code
async def get_property_list( self, request: GetPropertyListRequest, ) -> TaskResult[GetPropertyListResponse]: """ Get a property list with optional resolution. When resolve=true, returns the list of resolved property identifiers. Use this to get the actual properties that match the list's filters. Args: request: Request parameters including list_id and resolve flag Returns: TaskResult containing GetPropertyListResponse with identifiers """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_property_list", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_property_list(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_property_list", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetPropertyListResponse)Get a property list with optional resolution.
When resolve=true, returns the list of resolved property identifiers. Use this to get the actual properties that match the list's filters.
Args
request- Request parameters including list_id and resolve flag
Returns
TaskResult containing GetPropertyListResponse with identifiers
async def get_signals(self,
request: GetSignalsRequest) ‑> TaskResult[GetSignalsResponse]-
Expand source code
async def get_signals( self, request: GetSignalsRequest, ) -> TaskResult[GetSignalsResponse]: """ Get Signals. Args: request: Request parameters Returns: TaskResult containing GetSignalsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_signals", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.get_signals(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="get_signals", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, GetSignalsResponse)Get Signals.
Args
request- Request parameters
Returns
TaskResult containing GetSignalsResponse
def get_webhook_url(self, task_type: str, operation_id: str) ‑> str-
Expand source code
def get_webhook_url(self, task_type: str, operation_id: str) -> str: """Generate webhook URL for a task.""" if not self.webhook_url_template: raise ValueError("webhook_url_template not configured") return self.webhook_url_template.format( agent_id=self.agent_config.id, task_type=task_type, operation_id=operation_id, )Generate webhook URL for a task.
async def handle_webhook(self,
payload: dict[str, Any] | Task | TaskStatusUpdateEvent,
task_type: str,
operation_id: str,
signature: str | None = None,
timestamp: str | None = None) ‑> TaskResult[AdcpAsyncResponseData]-
Expand source code
async def handle_webhook( self, payload: dict[str, Any] | Task | TaskStatusUpdateEvent, task_type: str, operation_id: str, signature: str | None = None, timestamp: str | None = None, ) -> TaskResult[AdcpAsyncResponseData]: """ Handle incoming webhook and return typed result. This method provides a unified interface for handling webhooks from both MCP and A2A protocols: - MCP Webhooks: HTTP POST with dict payload, optional HMAC signature - A2A Webhooks: Task or TaskStatusUpdateEvent objects based on status The method automatically detects the protocol type and routes to the appropriate handler. Both protocols return a consistent TaskResult structure with typed AdCP response data. Args: payload: Webhook payload - one of: - dict[str, Any]: MCP webhook payload from HTTP POST - Task: A2A webhook for terminated statuses (completed, failed) - TaskStatusUpdateEvent: A2A webhook for intermediate statuses (working, input-required, submitted) task_type: Task type from application routing (e.g., "get_products"). Applications should extract this from URL routing pattern: /webhook/{task_type}/{agent_id}/{operation_id} operation_id: Operation identifier from application routing. Used to correlate webhook notifications with original task submission. signature: Optional HMAC-SHA256 signature for MCP webhook verification (X-AdCP-Signature header). Ignored for A2A webhooks. timestamp: Optional timestamp for MCP webhook signature verification (X-AdCP-Timestamp header). Required when signature is provided. Returns: TaskResult with parsed task-specific response data. The structure is identical regardless of protocol. Raises: ADCPWebhookSignatureError: If MCP signature verification fails ValidationError: If MCP payload doesn't match WebhookPayload schema Note: task_type and operation_id were deprecated from the webhook payload per AdCP specification. Applications must extract these from URL routing and pass them explicitly. Examples: MCP webhook (HTTP endpoint): >>> @app.post("/webhook/{task_type}/{agent_id}/{operation_id}") >>> async def webhook_handler(task_type: str, operation_id: str, request: Request): >>> payload = await request.json() >>> signature = request.headers.get("X-AdCP-Signature") >>> timestamp = request.headers.get("X-AdCP-Timestamp") >>> result = await client.handle_webhook( >>> payload, task_type, operation_id, signature, timestamp >>> ) >>> if result.success: >>> print(f"Task completed: {result.data}") A2A webhook with Task (terminated status): >>> async def on_task_completed(task: Task): >>> # Extract task_type and operation_id from your app's task tracking >>> task_type = your_task_registry.get_type(task.id) >>> operation_id = your_task_registry.get_operation_id(task.id) >>> result = await client.handle_webhook( >>> task, task_type, operation_id >>> ) >>> if result.success: >>> print(f"Task completed: {result.data}") A2A webhook with TaskStatusUpdateEvent (intermediate status): >>> async def on_task_update(event: TaskStatusUpdateEvent): >>> # Extract task_type and operation_id from your app's task tracking >>> task_type = your_task_registry.get_type(event.task_id) >>> operation_id = your_task_registry.get_operation_id(event.task_id) >>> result = await client.handle_webhook( >>> event, task_type, operation_id >>> ) >>> if result.status == GeneratedTaskStatus.working: >>> print(f"Task still working: {result.metadata.get('message')}") """ # Detect protocol type and route to appropriate handler if isinstance(payload, (Task, TaskStatusUpdateEvent)): # A2A webhook (Task or TaskStatusUpdateEvent) return await self._handle_a2a_webhook(payload, task_type, operation_id) else: # MCP webhook (dict payload) return await self._handle_mcp_webhook( payload, task_type, operation_id, signature, timestamp )Handle incoming webhook and return typed result.
This method provides a unified interface for handling webhooks from both MCP and A2A protocols:
- MCP Webhooks: HTTP POST with dict payload, optional HMAC signature
- A2A Webhooks: Task or TaskStatusUpdateEvent objects based on status
The method automatically detects the protocol type and routes to the appropriate handler. Both protocols return a consistent TaskResult structure with typed AdCP response data.
Args
payload- Webhook payload - one of: - dict[str, Any]: MCP webhook payload from HTTP POST - Task: A2A webhook for terminated statuses (completed, failed) - TaskStatusUpdateEvent: A2A webhook for intermediate statuses (working, input-required, submitted)
task_type- Task type from application routing (e.g., "get_products"). Applications should extract this from URL routing pattern: /webhook/{task_type}/{agent_id}/{operation_id}
operation_id- Operation identifier from application routing. Used to correlate webhook notifications with original task submission.
signature- Optional HMAC-SHA256 signature for MCP webhook verification (X-AdCP-Signature header). Ignored for A2A webhooks.
timestamp- Optional timestamp for MCP webhook signature verification (X-AdCP-Timestamp header). Required when signature is provided.
Returns
TaskResult with parsed task-specific response data. The structure is identical regardless of protocol.
Raises
ADCPWebhookSignatureError- If MCP signature verification fails
ValidationError- If MCP payload doesn't match WebhookPayload schema
Note
task_type and operation_id were deprecated from the webhook payload per AdCP specification. Applications must extract these from URL routing and pass them explicitly.
Examples
MCP webhook (HTTP endpoint):
>>> @app.post("/webhook/{task_type}/{agent_id}/{operation_id}") >>> async def webhook_handler(task_type: str, operation_id: str, request: Request): >>> payload = await request.json() >>> signature = request.headers.get("X-AdCP-Signature") >>> timestamp = request.headers.get("X-AdCP-Timestamp") >>> result = await client.handle_webhook( >>> payload, task_type, operation_id, signature, timestamp >>> ) >>> if result.success: >>> print(f"Task completed: {result.data}")A2A webhook with Task (terminated status):
>>> async def on_task_completed(task: Task): >>> # Extract task_type and operation_id from your app's task tracking >>> task_type = your_task_registry.get_type(task.id) >>> operation_id = your_task_registry.get_operation_id(task.id) >>> result = await client.handle_webhook( >>> task, task_type, operation_id >>> ) >>> if result.success: >>> print(f"Task completed: {result.data}")A2A webhook with TaskStatusUpdateEvent (intermediate status):
>>> async def on_task_update(event: TaskStatusUpdateEvent): >>> # Extract task_type and operation_id from your app's task tracking >>> task_type = your_task_registry.get_type(event.task_id) >>> operation_id = your_task_registry.get_operation_id(event.task_id) >>> result = await client.handle_webhook( >>> event, task_type, operation_id >>> ) >>> if result.status == GeneratedTaskStatus.working: >>> print(f"Task still working: {result.metadata.get('message')}") async def list_accounts(self,
request: ListAccountsRequest) ‑> TaskResult[ListAccountsResponse]-
Expand source code
async def list_accounts( self, request: ListAccountsRequest, ) -> TaskResult[ListAccountsResponse]: """ List Accounts. Args: request: Request parameters Returns: TaskResult containing ListAccountsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_accounts", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.list_accounts(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_accounts", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ListAccountsResponse)List Accounts.
Args
request- Request parameters
Returns
TaskResult containing ListAccountsResponse
async def list_content_standards(self, request: ListContentStandardsRequest) ‑> TaskResult[ListContentStandardsResponse]-
Expand source code
async def list_content_standards( self, request: ListContentStandardsRequest, ) -> TaskResult[ListContentStandardsResponse]: """ List content standards configurations. Args: request: Request parameters including optional filters Returns: TaskResult containing ListContentStandardsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_content_standards", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.list_content_standards(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_content_standards", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ListContentStandardsResponse)List content standards configurations.
Args
request- Request parameters including optional filters
Returns
TaskResult containing ListContentStandardsResponse
async def list_creative_formats(self,
request: ListCreativeFormatsRequest,
fetch_previews: bool = False,
preview_output_format: str = 'url') ‑> TaskResult[ListCreativeFormatsResponse]-
Expand source code
async def list_creative_formats( self, request: ListCreativeFormatsRequest, fetch_previews: bool = False, preview_output_format: str = "url", ) -> TaskResult[ListCreativeFormatsResponse]: """ List supported creative formats. Args: request: Request parameters fetch_previews: If True, generate preview URLs for each format using sample manifests (uses batch API for 5-10x performance improvement) preview_output_format: "url" for iframe URLs (default), "html" for direct embedding (2-3x faster, no iframe overhead) Returns: TaskResult containing ListCreativeFormatsResponse with optional preview URLs in metadata """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_creative_formats", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.list_creative_formats(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_creative_formats", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) result: TaskResult[ListCreativeFormatsResponse] = self.adapter._parse_response( raw_result, ListCreativeFormatsResponse ) if fetch_previews and result.success and result.data: from adcp.utils.preview_cache import add_preview_urls_to_formats formats_with_previews = await add_preview_urls_to_formats( result.data.formats, self, use_batch=True, output_format=preview_output_format, ) result.metadata = result.metadata or {} result.metadata["formats_with_previews"] = formats_with_previews return resultList supported creative formats.
Args
request- Request parameters
fetch_previews- If True, generate preview URLs for each format using sample manifests (uses batch API for 5-10x performance improvement)
preview_output_format- "url" for iframe URLs (default), "html" for direct embedding (2-3x faster, no iframe overhead)
Returns
TaskResult containing ListCreativeFormatsResponse with optional preview URLs in metadata
async def list_creatives(self,
request: ListCreativesRequest) ‑> TaskResult[ListCreativesResponse]-
Expand source code
async def list_creatives( self, request: ListCreativesRequest, ) -> TaskResult[ListCreativesResponse]: """ List Creatives. Args: request: Request parameters Returns: TaskResult containing ListCreativesResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_creatives", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.list_creatives(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_creatives", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ListCreativesResponse)List Creatives.
Args
request- Request parameters
Returns
TaskResult containing ListCreativesResponse
async def list_property_lists(self, request: ListPropertyListsRequest) ‑> TaskResult[ListPropertyListsResponse]-
Expand source code
async def list_property_lists( self, request: ListPropertyListsRequest, ) -> TaskResult[ListPropertyListsResponse]: """ List property lists owned by a principal. Retrieves metadata for all property lists, optionally filtered by principal or pagination parameters. Args: request: Request parameters with optional filtering Returns: TaskResult containing ListPropertyListsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_property_lists", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.list_property_lists(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="list_property_lists", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ListPropertyListsResponse)List property lists owned by a principal.
Retrieves metadata for all property lists, optionally filtered by principal or pagination parameters.
Args
request- Request parameters with optional filtering
Returns
TaskResult containing ListPropertyListsResponse
async def list_tools(self) ‑> list[str]-
Expand source code
async def list_tools(self) -> list[str]: """ List available tools from the agent. Returns: List of tool names """ return await self.adapter.list_tools()List available tools from the agent.
Returns
List of tool names
async def log_event(self,
request: LogEventRequest) ‑> TaskResult[LogEventResponse]-
Expand source code
async def log_event( self, request: LogEventRequest, ) -> TaskResult[LogEventResponse]: """ Log Event. Args: request: Request parameters Returns: TaskResult containing LogEventResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="log_event", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.log_event(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="log_event", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, LogEventResponse)Log Event.
Args
request- Request parameters
Returns
TaskResult containing LogEventResponse
async def preview_creative(self,
request: PreviewCreativeRequest) ‑> TaskResult[PreviewCreativeResponse]-
Expand source code
async def preview_creative( self, request: PreviewCreativeRequest, ) -> TaskResult[PreviewCreativeResponse]: """ Generate preview of a creative manifest. Args: request: Request parameters Returns: TaskResult containing PreviewCreativeResponse with preview URLs """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="preview_creative", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.preview_creative(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="preview_creative", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, PreviewCreativeResponse)Generate preview of a creative manifest.
Args
request- Request parameters
Returns
TaskResult containing PreviewCreativeResponse with preview URLs
async def provide_performance_feedback(self,
request: ProvidePerformanceFeedbackRequest) ‑> TaskResult[ProvidePerformanceFeedbackResponse]-
Expand source code
async def provide_performance_feedback( self, request: ProvidePerformanceFeedbackRequest, ) -> TaskResult[ProvidePerformanceFeedbackResponse]: """ Provide Performance Feedback. Args: request: Request parameters Returns: TaskResult containing ProvidePerformanceFeedbackResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="provide_performance_feedback", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.provide_performance_feedback(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="provide_performance_feedback", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ProvidePerformanceFeedbackResponse)Provide Performance Feedback.
Args
request- Request parameters
Returns
TaskResult containing ProvidePerformanceFeedbackResponse
async def si_get_offering(self, request: SiGetOfferingRequest) ‑> TaskResult[SiGetOfferingResponse]-
Expand source code
async def si_get_offering( self, request: SiGetOfferingRequest, ) -> TaskResult[SiGetOfferingResponse]: """ Get sponsored intelligence offering. Retrieves product/service offerings that can be presented in a sponsored intelligence session. Args: request: Request parameters including brand context Returns: TaskResult containing SiGetOfferingResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_get_offering", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.si_get_offering(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_get_offering", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SiGetOfferingResponse)Get sponsored intelligence offering.
Retrieves product/service offerings that can be presented in a sponsored intelligence session.
Args
request- Request parameters including brand context
Returns
TaskResult containing SiGetOfferingResponse
async def si_initiate_session(self, request: SiInitiateSessionRequest) ‑> TaskResult[SiInitiateSessionResponse]-
Expand source code
async def si_initiate_session( self, request: SiInitiateSessionRequest, ) -> TaskResult[SiInitiateSessionResponse]: """ Initiate a sponsored intelligence session. Starts a conversational brand experience session with a user. Args: request: Request parameters including identity and context Returns: TaskResult containing SiInitiateSessionResponse with session_id """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_initiate_session", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.si_initiate_session(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_initiate_session", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SiInitiateSessionResponse)Initiate a sponsored intelligence session.
Starts a conversational brand experience session with a user.
Args
request- Request parameters including identity and context
Returns
TaskResult containing SiInitiateSessionResponse with session_id
async def si_send_message(self, request: SiSendMessageRequest) ‑> TaskResult[SiSendMessageResponse]-
Expand source code
async def si_send_message( self, request: SiSendMessageRequest, ) -> TaskResult[SiSendMessageResponse]: """ Send a message in a sponsored intelligence session. Continues the conversation in an active SI session. Args: request: Request parameters including session_id and message Returns: TaskResult containing SiSendMessageResponse with brand response """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_send_message", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.si_send_message(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_send_message", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SiSendMessageResponse)Send a message in a sponsored intelligence session.
Continues the conversation in an active SI session.
Args
request- Request parameters including session_id and message
Returns
TaskResult containing SiSendMessageResponse with brand response
async def si_terminate_session(self, request: SiTerminateSessionRequest) ‑> TaskResult[SiTerminateSessionResponse]-
Expand source code
async def si_terminate_session( self, request: SiTerminateSessionRequest, ) -> TaskResult[SiTerminateSessionResponse]: """ Terminate a sponsored intelligence session. Ends an active SI session, optionally with follow-up actions. Args: request: Request parameters including session_id and termination context Returns: TaskResult containing SiTerminateSessionResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_terminate_session", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.si_terminate_session(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="si_terminate_session", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SiTerminateSessionResponse)Terminate a sponsored intelligence session.
Ends an active SI session, optionally with follow-up actions.
Args
request- Request parameters including session_id and termination context
Returns
TaskResult containing SiTerminateSessionResponse
async def sync_accounts(self,
request: SyncAccountsRequest) ‑> TaskResult[SyncAccountsResponse]-
Expand source code
async def sync_accounts( self, request: SyncAccountsRequest, ) -> TaskResult[SyncAccountsResponse]: """ Sync Accounts. Args: request: Request parameters Returns: TaskResult containing SyncAccountsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_accounts", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.sync_accounts(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_accounts", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SyncAccountsResponse)Sync Accounts.
Args
request- Request parameters
Returns
TaskResult containing SyncAccountsResponse
async def sync_creatives(self,
request: SyncCreativesRequest) ‑> TaskResult[SyncCreativesResponse]-
Expand source code
async def sync_creatives( self, request: SyncCreativesRequest, ) -> TaskResult[SyncCreativesResponse]: """ Sync Creatives. Args: request: Request parameters Returns: TaskResult containing SyncCreativesResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_creatives", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.sync_creatives(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_creatives", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SyncCreativesResponse)Sync Creatives.
Args
request- Request parameters
Returns
TaskResult containing SyncCreativesResponse
async def sync_event_sources(self,
request: SyncEventSourcesRequest) ‑> TaskResult[SyncEventSourcesResponse]-
Expand source code
async def sync_event_sources( self, request: SyncEventSourcesRequest, ) -> TaskResult[SyncEventSourcesResponse]: """ Sync Event Sources. Args: request: Request parameters Returns: TaskResult containing SyncEventSourcesResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_event_sources", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.sync_event_sources(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="sync_event_sources", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, SyncEventSourcesResponse)Sync Event Sources.
Args
request- Request parameters
Returns
TaskResult containing SyncEventSourcesResponse
async def update_content_standards(self, request: UpdateContentStandardsRequest) ‑> TaskResult[UpdateContentStandardsResponse]-
Expand source code
async def update_content_standards( self, request: UpdateContentStandardsRequest, ) -> TaskResult[UpdateContentStandardsResponse]: """ Update a content standards configuration. Args: request: Request parameters including standards_id and updates Returns: TaskResult containing UpdateContentStandardsResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_content_standards", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.update_content_standards(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_content_standards", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, UpdateContentStandardsResponse)Update a content standards configuration.
Args
request- Request parameters including standards_id and updates
Returns
TaskResult containing UpdateContentStandardsResponse
async def update_media_buy(self,
request: UpdateMediaBuyRequest) ‑> TaskResult[UpdateMediaBuyResponse]-
Expand source code
async def update_media_buy( self, request: UpdateMediaBuyRequest, ) -> TaskResult[UpdateMediaBuyResponse]: """ Update an existing media buy reservation. Modifies a previously created media buy by updating packages or publisher properties. The update operation uses discriminated unions to specify what to change - either package details or targeting properties. Args: request: Media buy update parameters including: - media_buy_id: Identifier from create_media_buy response - updates: Discriminated union specifying update type: * UpdateMediaBuyPackagesRequest: Modify package selections * UpdateMediaBuyPropertiesRequest: Change targeting properties Returns: TaskResult containing UpdateMediaBuyResponse with: - media_buy_id: The updated media buy identifier - status: Updated state of the media buy - packages: Updated package configurations - Additional platform-specific metadata Example: >>> from adcp import ADCPClient, UpdateMediaBuyPackagesRequest >>> client = ADCPClient(agent_config) >>> request = UpdateMediaBuyPackagesRequest( ... media_buy_id="mb_123", ... packages=[updated_package] ... ) >>> result = await client.update_media_buy(request) >>> if result.success: ... updated_packages = result.data.packages """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_media_buy", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.update_media_buy(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_media_buy", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, UpdateMediaBuyResponse)Update an existing media buy reservation.
Modifies a previously created media buy by updating packages or publisher properties. The update operation uses discriminated unions to specify what to change - either package details or targeting properties.
Args
request- Media buy update parameters including: - media_buy_id: Identifier from create_media_buy response - updates: Discriminated union specifying update type: * UpdateMediaBuyPackagesRequest: Modify package selections * UpdateMediaBuyPropertiesRequest: Change targeting properties
Returns
TaskResult containing UpdateMediaBuyResponse with: - media_buy_id: The updated media buy identifier - status: Updated state of the media buy - packages: Updated package configurations - Additional platform-specific metadata
Example
>>> from adcp import ADCPClient, UpdateMediaBuyPackagesRequest >>> client = ADCPClient(agent_config) >>> request = UpdateMediaBuyPackagesRequest( ... media_buy_id="mb_123", ... packages=[updated_package] ... ) >>> result = await client.update_media_buy(request) >>> if result.success: ... updated_packages = result.data.packages async def update_property_list(self, request: UpdatePropertyListRequest) ‑> TaskResult[UpdatePropertyListResponse]-
Expand source code
async def update_property_list( self, request: UpdatePropertyListRequest, ) -> TaskResult[UpdatePropertyListResponse]: """ Update a property list. Modifies the filters, brand manifest, or other parameters of an existing property list. Args: request: Request parameters with list_id and updates Returns: TaskResult containing UpdatePropertyListResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_property_list", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.update_property_list(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="update_property_list", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, UpdatePropertyListResponse)Update a property list.
Modifies the filters, brand manifest, or other parameters of an existing property list.
Args
request- Request parameters with list_id and updates
Returns
TaskResult containing UpdatePropertyListResponse
async def validate_content_delivery(self, request: ValidateContentDeliveryRequest) ‑> TaskResult[ValidateContentDeliveryResponse]-
Expand source code
async def validate_content_delivery( self, request: ValidateContentDeliveryRequest, ) -> TaskResult[ValidateContentDeliveryResponse]: """ Validate content delivery against standards. Validates that ad delivery records comply with content standards. Args: request: Request parameters including delivery records Returns: TaskResult containing ValidateContentDeliveryResponse """ operation_id = create_operation_id() params = request.model_dump(exclude_none=True) self._emit_activity( Activity( type=ActivityType.PROTOCOL_REQUEST, operation_id=operation_id, agent_id=self.agent_config.id, task_type="validate_content_delivery", timestamp=datetime.now(timezone.utc).isoformat(), ) ) raw_result = await self.adapter.validate_content_delivery(params) self._emit_activity( Activity( type=ActivityType.PROTOCOL_RESPONSE, operation_id=operation_id, agent_id=self.agent_config.id, task_type="validate_content_delivery", status=raw_result.status, timestamp=datetime.now(timezone.utc).isoformat(), ) ) return self.adapter._parse_response(raw_result, ValidateContentDeliveryResponse)Validate content delivery against standards.
Validates that ad delivery records comply with content standards.
Args
request- Request parameters including delivery records
Returns
TaskResult containing ValidateContentDeliveryResponse
class ADCPConnectionError (message: str, agent_id: str | None = None, agent_uri: str | None = None)-
Expand source code
class ADCPConnectionError(ADCPError): """Connection to agent failed.""" def __init__(self, message: str, agent_id: str | None = None, agent_uri: str | None = None): """Initialize connection error.""" suggestion = ( "Check that the agent URI is correct and the agent is running.\n" " Try testing with: python -m adcp test --config <agent-id>" ) super().__init__(message, agent_id, agent_uri, suggestion)Connection to agent failed.
Initialize connection error.
Ancestors
- ADCPError
- builtins.Exception
- builtins.BaseException
class ADCPError (message: str,
agent_id: str | None = None,
agent_uri: str | None = None,
suggestion: str | None = None)-
Expand source code
class ADCPError(Exception): """Base exception for all AdCP client errors.""" def __init__( self, message: str, agent_id: str | None = None, agent_uri: str | None = None, suggestion: str | None = None, ): """Initialize exception with context.""" self.message = message self.agent_id = agent_id self.agent_uri = agent_uri self.suggestion = suggestion full_message = message if agent_id: full_message = f"[Agent: {agent_id}] {full_message}" if agent_uri: full_message = f"{full_message}\n URI: {agent_uri}" if suggestion: full_message = f"{full_message}\n 💡 {suggestion}" super().__init__(full_message)Base exception for all AdCP client errors.
Initialize exception with context.
Ancestors
- builtins.Exception
- builtins.BaseException
Subclasses
class ADCPMultiAgentClient (agents: list[AgentConfig],
webhook_url_template: str | None = None,
webhook_secret: str | None = None,
on_activity: Callable[[Activity], None] | None = None,
handlers: dict[str, Callable[..., Any]] | None = None)-
Expand source code
class ADCPMultiAgentClient: """Client for managing multiple AdCP agents.""" def __init__( self, agents: list[AgentConfig], webhook_url_template: str | None = None, webhook_secret: str | None = None, on_activity: Callable[[Activity], None] | None = None, handlers: dict[str, Callable[..., Any]] | None = None, ): """ Initialize multi-agent client. Args: agents: List of agent configurations webhook_url_template: Template for webhook URLs webhook_secret: Secret for webhook verification on_activity: Callback for activity events handlers: Task completion handlers """ self.agents = { agent.id: ADCPClient( agent, webhook_url_template=webhook_url_template, webhook_secret=webhook_secret, on_activity=on_activity, ) for agent in agents } self.handlers = handlers or {} def agent(self, agent_id: str) -> ADCPClient: """Get client for specific agent.""" if agent_id not in self.agents: raise ValueError(f"Agent not found: {agent_id}") return self.agents[agent_id] @property def agent_ids(self) -> list[str]: """Get list of agent IDs.""" return list(self.agents.keys()) async def close(self) -> None: """Close all agent clients and clean up resources.""" import asyncio logger.debug("Closing all agent clients in multi-agent client") close_tasks = [client.close() for client in self.agents.values()] await asyncio.gather(*close_tasks, return_exceptions=True) async def __aenter__(self) -> ADCPMultiAgentClient: """Async context manager entry.""" return self async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: """Async context manager exit.""" await self.close() async def get_products( self, request: GetProductsRequest, ) -> list[TaskResult[GetProductsResponse]]: """ Execute get_products across all agents in parallel. Args: request: Request parameters Returns: List of TaskResults containing GetProductsResponse for each agent """ import asyncio tasks = [agent.get_products(request) for agent in self.agents.values()] return await asyncio.gather(*tasks) @classmethod def from_env(cls) -> ADCPMultiAgentClient: """Create client from environment variables.""" agents_json = os.getenv("ADCP_AGENTS") if not agents_json: raise ValueError("ADCP_AGENTS environment variable not set") agents_data = json.loads(agents_json) agents = [AgentConfig(**agent) for agent in agents_data] return cls( agents=agents, webhook_url_template=os.getenv("WEBHOOK_URL_TEMPLATE"), webhook_secret=os.getenv("WEBHOOK_SECRET"), )Client for managing multiple AdCP agents.
Initialize multi-agent client.
Args
agents- List of agent configurations
webhook_url_template- Template for webhook URLs
webhook_secret- Secret for webhook verification
on_activity- Callback for activity events
handlers- Task completion handlers
Static methods
def from_env() ‑> ADCPMultiAgentClient-
Create client from environment variables.
Instance variables
prop agent_ids : list[str]-
Expand source code
@property def agent_ids(self) -> list[str]: """Get list of agent IDs.""" return list(self.agents.keys())Get list of agent IDs.
Methods
def agent(self, agent_id: str) ‑> ADCPClient-
Expand source code
def agent(self, agent_id: str) -> ADCPClient: """Get client for specific agent.""" if agent_id not in self.agents: raise ValueError(f"Agent not found: {agent_id}") return self.agents[agent_id]Get client for specific agent.
async def close(self) ‑> None-
Expand source code
async def close(self) -> None: """Close all agent clients and clean up resources.""" import asyncio logger.debug("Closing all agent clients in multi-agent client") close_tasks = [client.close() for client in self.agents.values()] await asyncio.gather(*close_tasks, return_exceptions=True)Close all agent clients and clean up resources.
async def get_products(self,
request: GetProductsRequest) ‑> list[TaskResult[GetProductsResponse]]-
Expand source code
async def get_products( self, request: GetProductsRequest, ) -> list[TaskResult[GetProductsResponse]]: """ Execute get_products across all agents in parallel. Args: request: Request parameters Returns: List of TaskResults containing GetProductsResponse for each agent """ import asyncio tasks = [agent.get_products(request) for agent in self.agents.values()] return await asyncio.gather(*tasks)Execute get_products across all agents in parallel.
Args
request- Request parameters
Returns
List of TaskResults containing GetProductsResponse for each agent
class ADCPProtocolError (message: str, agent_id: str | None = None, protocol: str | None = None)-
Expand source code
class ADCPProtocolError(ADCPError): """Protocol-level error (malformed response, unexpected format).""" def __init__(self, message: str, agent_id: str | None = None, protocol: str | None = None): """Initialize protocol error.""" suggestion = ( f"The agent returned an unexpected {protocol} response format." if protocol else "Unexpected response format." ) suggestion += "\n Enable debug mode to see the full request/response." super().__init__(message, agent_id, None, suggestion)Protocol-level error (malformed response, unexpected format).
Initialize protocol error.
Ancestors
- ADCPError
- builtins.Exception
- builtins.BaseException
class ADCPTimeoutError (message: str,
agent_id: str | None = None,
agent_uri: str | None = None,
timeout: float | None = None)-
Expand source code
class ADCPTimeoutError(ADCPError): """Request timed out.""" def __init__( self, message: str, agent_id: str | None = None, agent_uri: str | None = None, timeout: float | None = None, ): """Initialize timeout error.""" suggestion = ( f"The request took longer than {timeout}s." if timeout else "The request timed out." ) suggestion += "\n Try increasing the timeout value or check if the agent is overloaded." super().__init__(message, agent_id, agent_uri, suggestion)Request timed out.
Initialize timeout error.
Ancestors
- ADCPError
- builtins.Exception
- builtins.BaseException
class ADCPToolNotFoundError (tool_name: str,
agent_id: str | None = None,
available_tools: list[str] | None = None)-
Expand source code
class ADCPToolNotFoundError(ADCPError): """Requested tool not found on agent.""" def __init__( self, tool_name: str, agent_id: str | None = None, available_tools: list[str] | None = None ): """Initialize tool not found error.""" message = f"Tool '{tool_name}' not found on agent" suggestion = "List available tools with: python -m adcp list-tools --config <agent-id>" if available_tools: tools_list = ", ".join(available_tools[:5]) if len(available_tools) > 5: tools_list += f", ... ({len(available_tools)} total)" suggestion = f"Available tools: {tools_list}" super().__init__(message, agent_id, None, suggestion)Requested tool not found on agent.
Initialize tool not found error.
Ancestors
- ADCPError
- builtins.Exception
- builtins.BaseException
class ADCPWebhookError (message: str,
agent_id: str | None = None,
agent_uri: str | None = None,
suggestion: str | None = None)-
Expand source code
class ADCPWebhookError(ADCPError): """Webhook handling error."""Webhook handling error.
Initialize exception with context.
Ancestors
- ADCPError
- builtins.Exception
- builtins.BaseException
Subclasses
class ADCPWebhookSignatureError (message: str = 'Invalid webhook signature', agent_id: str | None = None)-
Expand source code
class ADCPWebhookSignatureError(ADCPWebhookError): """Webhook signature verification failed.""" def __init__(self, message: str = "Invalid webhook signature", agent_id: str | None = None): """Initialize webhook signature error.""" suggestion = ( "Verify that the webhook_secret matches the secret configured on the agent.\n" " Webhook signatures use HMAC-SHA256 for security." ) super().__init__(message, agent_id, None, suggestion)Webhook signature verification failed.
Initialize webhook signature error.
Ancestors
- ADCPWebhookError
- ADCPError
- builtins.Exception
- builtins.BaseException
class ActivateSignalRequest (**data: Any)-
Expand source code
class ActivateSignalRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None deployments: Annotated[ list[destination.Destination], Field( description='Target deployment(s) for activation. If the authenticated caller matches one of these deployment targets, activation keys will be included in the response.', min_length=1, ), ] ext: ext_1.ExtensionObject | None = None signal_agent_segment_id: Annotated[ str, Field(description='The universal identifier for the signal to activate') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar deployments : list[Destination]var ext : ExtensionObject | Nonevar model_configvar signal_agent_segment_id : str
Inherited members
class ActivateSignalResponse (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class ActivateSignalResponse(RootModel[ActivateSignalResponse1 | ActivateSignalResponse2]): root: Annotated[ ActivateSignalResponse1 | ActivateSignalResponse2, Field( description='Response payload for activate_signal task. Returns either complete success data OR error information, never both. This enforces atomic operation semantics - the signal is either fully activated or not activated at all.', title='Activate Signal Response', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[ActivateSignalResponse1, ActivateSignalResponse2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : ActivateSignalResponse1 | ActivateSignalResponse2
class ActivateSignalSuccessResponse (**data: Any)-
Expand source code
class ActivateSignalResponse1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None deployments: Annotated[ list[deployment.Deployment], Field(description='Array of deployment results for each deployment target'), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar deployments : list[Deployment]var ext : ExtensionObject | Nonevar model_config
Inherited members
class ActivateSignalErrorResponse (**data: Any)-
Expand source code
class ActivateSignalResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error], Field( description='Array of errors explaining why activation failed (e.g., platform connectivity issues, signal definition problems, authentication failures)', min_length=1, ), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_config
Inherited members
class AdagentsNotFoundError (publisher_domain: str)-
Expand source code
class AdagentsNotFoundError(AdagentsValidationError): """adagents.json file not found (404).""" def __init__(self, publisher_domain: str): """Initialize not found error.""" message = f"adagents.json not found for domain: {publisher_domain}" suggestion = ( "Verify that the publisher has deployed adagents.json to:\n" f" https://{publisher_domain}/.well-known/adagents.json" ) super().__init__(message, None, None, suggestion)adagents.json file not found (404).
Initialize not found error.
Ancestors
- AdagentsValidationError
- ADCPError
- builtins.Exception
- builtins.BaseException
class AdagentsTimeoutError (publisher_domain: str, timeout: float)-
Expand source code
class AdagentsTimeoutError(AdagentsValidationError): """Request for adagents.json timed out.""" def __init__(self, publisher_domain: str, timeout: float): """Initialize timeout error.""" message = f"Request to fetch adagents.json timed out after {timeout}s" suggestion = ( "The publisher's server may be slow or unresponsive.\n" " Try increasing the timeout value or check the domain is correct." ) super().__init__(message, None, None, suggestion)Request for adagents.json timed out.
Initialize timeout error.
Ancestors
- AdagentsValidationError
- ADCPError
- builtins.Exception
- builtins.BaseException
class AdagentsValidationError (message: str,
agent_id: str | None = None,
agent_uri: str | None = None,
suggestion: str | None = None)-
Expand source code
class AdagentsValidationError(ADCPError): """Base error for adagents.json validation issues."""Base error for adagents.json validation issues.
Initialize exception with context.
Ancestors
- ADCPError
- builtins.Exception
- builtins.BaseException
Subclasses
class AgentConfig (**data: Any)-
Expand source code
class AgentConfig(BaseModel): """Agent configuration.""" id: str agent_uri: str protocol: Protocol auth_token: str | None = None requires_auth: bool = False auth_header: str = "x-adcp-auth" # Header name for authentication auth_type: str = "token" # "token" for direct value, "bearer" for "Bearer {token}" timeout: float = 30.0 # Request timeout in seconds mcp_transport: str = ( "streamable_http" # "streamable_http" (default, modern) or "sse" (legacy fallback) ) debug: bool = False # Enable debug mode to capture request/response details @field_validator("agent_uri") @classmethod def validate_agent_uri(cls, v: str) -> str: """Validate agent URI format.""" if not v: raise ValueError("agent_uri cannot be empty") if not v.startswith(("http://", "https://")): raise ValueError( f"agent_uri must start with http:// or https://, got: {v}\n" "Example: https://agent.example.com" ) # Remove trailing slash for consistency return v.rstrip("/") @field_validator("timeout") @classmethod def validate_timeout(cls, v: float) -> float: """Validate timeout is reasonable.""" if v <= 0: raise ValueError(f"timeout must be positive, got: {v}") if v > 300: # 5 minutes raise ValueError( f"timeout is very large ({v}s). Consider a value under 300 seconds.\n" "Large timeouts can cause long hangs if agent is unresponsive." ) return v @field_validator("mcp_transport") @classmethod def validate_mcp_transport(cls, v: str) -> str: """Validate MCP transport type.""" valid_transports = ["streamable_http", "sse"] if v not in valid_transports: raise ValueError( f"mcp_transport must be one of {valid_transports}, got: {v}\n" "Use 'streamable_http' for modern agents (recommended)" ) return v @field_validator("auth_type") @classmethod def validate_auth_type(cls, v: str) -> str: """Validate auth type.""" valid_types = ["token", "bearer"] if v not in valid_types: raise ValueError( f"auth_type must be one of {valid_types}, got: {v}\n" "Use 'bearer' for OAuth2/standard Authorization header" ) return vAgent configuration.
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
- pydantic.main.BaseModel
Class variables
var agent_uri : strvar auth_header : strvar auth_token : str | Nonevar auth_type : strvar debug : boolvar id : strvar mcp_transport : strvar model_configvar protocol : Protocolvar requires_auth : boolvar timeout : float
Static methods
def validate_agent_uri(v: str) ‑> str-
Validate agent URI format.
def validate_auth_type(v: str) ‑> str-
Validate auth type.
def validate_mcp_transport(v: str) ‑> str-
Validate MCP transport type.
def validate_timeout(v: float) ‑> float-
Validate timeout is reasonable.
class AssetContentType (*args, **kwds)-
Expand source code
class AssetContentType(Enum): image = 'image' video = 'video' audio = 'audio' text = 'text' markdown = 'markdown' html = 'html' css = 'css' javascript = 'javascript' vast = 'vast' daast = 'daast' promoted_offerings = 'promoted_offerings' url = 'url' webhook = 'webhook'Create a collection of name/value pairs.
Example enumeration:
>>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3Access them by:
- attribute access::
>>> Color.RED <Color.RED: 1>- value lookup:
>>> Color(1) <Color.RED: 1>- name lookup:
>>> Color['RED'] <Color.RED: 1>Enumerations can be iterated over, and know how many members they have:
>>> len(Color) 3>>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.
Ancestors
- enum.Enum
Class variables
var audiovar cssvar daastvar htmlvar imagevar javascriptvar markdownvar promoted_offeringsvar textvar urlvar vastvar videovar webhook
class AuthorizationContext (properties: list[dict[str, Any]])-
Expand source code
class AuthorizationContext: """Authorization context for a publisher domain. Attributes: property_ids: List of property IDs the agent is authorized for property_tags: List of property tags the agent is authorized for raw_properties: Raw property data from adagents.json """ def __init__(self, properties: list[dict[str, Any]]): """Initialize from list of properties. Args: properties: List of property dictionaries from adagents.json """ self.property_ids: list[str] = [] self.property_tags: list[str] = [] self.raw_properties = properties # Extract property IDs and tags for prop in properties: if not isinstance(prop, dict): continue # Extract property ID (per AdCP v2 schema, the field is "property_id") prop_id = prop.get("property_id") if prop_id and isinstance(prop_id, str): self.property_ids.append(prop_id) # Extract tags tags = prop.get("tags", []) if isinstance(tags, list): for tag in tags: if isinstance(tag, str) and tag not in self.property_tags: self.property_tags.append(tag) def __repr__(self) -> str: return ( f"AuthorizationContext(" f"property_ids={self.property_ids}, " f"property_tags={self.property_tags})" )Authorization context for a publisher domain.
Attributes
property_ids- List of property IDs the agent is authorized for
property_tags- List of property tags the agent is authorized for
raw_properties- Raw property data from adagents.json
Initialize from list of properties.
Args
properties- List of property dictionaries from adagents.json
class BrandManifest (**data: Any)-
Expand source code
class BrandManifest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) assets: Annotated[ list[Asset] | None, Field( description='Brand asset library with explicit assets and tags. Assets are referenced inline with URLs pointing to CDN-hosted files.' ), ] = None avatar: Annotated[ Avatar | None, Field(description='Brand avatar configuration for visual conversational experiences'), ] = None colors: Annotated[ Colors | None, Field( description='Brand color palette. Each role accepts a single hex color or an array of hex colors for brands with multiple values per role.' ), ] = None contact: Annotated[Contact | None, Field(description='Brand contact information')] = None disclaimers: Annotated[ list[Disclaimer] | None, Field(description='Legal disclaimers or required text that must appear in creatives'), ] = None fonts: Annotated[Fonts | None, Field(description='Brand typography guidelines')] = None industry: Annotated[ str | None, Field( description="Industry or vertical (e.g., 'retail', 'automotive', 'finance', 'healthcare')" ), ] = None logos: Annotated[ list[Logo] | None, Field( description='Brand logo assets with structured fields for orientation, background compatibility, and variant type. Use the orientation, background, and variant enum fields for reliable filtering by creative agents.' ), ] = None metadata: Annotated[Metadata | None, Field(description='Additional brand metadata')] = None name: Annotated[str, Field(description='Brand or business name')] privacy_policy_url: Annotated[ AnyUrl | None, Field( description="URL to the brand's privacy policy. Used for consumer consent flows when personal data may be shared with the advertiser. AI platforms can use this to present explicit privacy choices to users before data handoff." ), ] = None product_catalog: Annotated[ ProductCatalog | None, Field( description='Product catalog information for e-commerce advertisers. Enables SKU-level creative generation and product selection.' ), ] = None tagline: Annotated[str | None, Field(description='Brand tagline or slogan')] = None target_audience: Annotated[ str | None, Field(description='Primary target audience description') ] = None tone: Annotated[ str | Tone | None, Field(description='Brand voice and messaging tone guidelines for creative agents.'), ] = None url: Annotated[ AnyUrl | None, Field( description='Primary brand URL for context and asset discovery. Creative agents can infer brand information from this URL.' ), ] = None voice: Annotated[ Voice | None, Field(description='Brand voice configuration for audio/conversational experiences'), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 assets : list[Asset] | Nonevar avatar : Avatar | Nonevar colors : Colors | Nonevar contact : Contact | Nonevar disclaimers : list[Disclaimer] | Nonevar fonts : Fonts | Nonevar industry : str | Nonevar logos : list[Logo] | Nonevar metadata : Metadata | Nonevar model_configvar name : strvar privacy_policy_url : pydantic.networks.AnyUrl | Nonevar product_catalog : ProductCatalog | Nonevar tagline : str | Nonevar target_audience : str | Nonevar tone : str | Tone | Nonevar url : pydantic.networks.AnyUrl | Nonevar voice : Voice | None
Inherited members
class BuildCreativeRequest (**data: Any)-
Expand source code
class BuildCreativeRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None creative_manifest: Annotated[ creative_manifest_1.CreativeManifest | None, Field( description='Creative manifest to transform or generate from. For pure generation, this should include the target format_id and any required input assets (e.g., promoted_offerings for generative formats). For transformation (e.g., resizing, reformatting), this is the complete creative to adapt.' ), ] = None ext: ext_1.ExtensionObject | None = None message: Annotated[ str | None, Field( description='Natural language instructions for the transformation or generation. For pure generation, this is the creative brief. For transformation, this provides guidance on how to adapt the creative.' ), ] = None target_format_id: Annotated[ format_id.FormatId, Field( description='Format ID to generate. The format definition specifies required input assets and output structure.' ), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar creative_manifest : CreativeManifest | Nonevar ext : ExtensionObject | Nonevar message : str | Nonevar model_configvar target_format_id : FormatId
Inherited members
class BuildCreativeResponse (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class BuildCreativeResponse(RootModel[BuildCreativeResponse1 | BuildCreativeResponse2]): root: Annotated[ BuildCreativeResponse1 | BuildCreativeResponse2, Field( description='Response containing the transformed or generated creative manifest, ready for use with preview_creative or sync_creatives. Returns either the complete creative manifest OR error information, never both.', title='Build Creative Response', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[BuildCreativeResponse1, BuildCreativeResponse2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : BuildCreativeResponse1 | BuildCreativeResponse2
class BuildCreativeSuccessResponse (**data: Any)-
Expand source code
class BuildCreativeResponse1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None creative_manifest: Annotated[ creative_manifest_1.CreativeManifest, Field(description='The generated or transformed creative manifest'), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar creative_manifest : CreativeManifestvar ext : ExtensionObject | Nonevar model_config
Inherited members
class BuildCreativeErrorResponse (**data: Any)-
Expand source code
class BuildCreativeResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error], Field( description='Array of errors explaining why creative generation failed', min_length=1 ), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_config
Inherited members
class CalibrateContentSuccessResponse (**data: Any)-
Expand source code
class CalibrateContentResponse1(AdCPBaseModel): confidence: Annotated[ float | None, Field(description='Model confidence in the verdict (0-1)', ge=0.0, le=1.0) ] = None errors: Annotated[ Any | None, Field(description='Field must not be present in success response') ] = None explanation: Annotated[ str | None, Field(description='Detailed natural language explanation of the decision') ] = None features: Annotated[ list[Feature] | None, Field(description='Per-feature breakdown with explanations') ] = None verdict: Annotated[ Verdict, Field(description='Overall pass/fail verdict for the content evaluation') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 confidence : float | Nonevar errors : typing.Any | Nonevar explanation : str | Nonevar features : list[Feature] | Nonevar model_configvar verdict : Verdict
Inherited members
class CalibrateContentErrorResponse (**data: Any)-
Expand source code
class CalibrateContentResponse2(AdCPBaseModel): errors: list[error.Error] verdict: Annotated[ Any | None, Field(description='Field must not be present in error response') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 errors : list[Error]var model_configvar verdict : typing.Any | None
Inherited members
class CpcPricingOption (**data: Any)-
Expand source code
class CpcPricingOption(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) currency: Annotated[ str, Field( description='ISO 4217 currency code', examples=['USD', 'EUR', 'GBP', 'JPY'], pattern='^[A-Z]{3}$', ), ] fixed_price: Annotated[ float | None, Field( description='Fixed price per click. If present, this is fixed pricing. If absent, auction-based.', ge=0.0, ), ] = None floor_price: Annotated[ float | None, Field( description='Minimum acceptable bid for auction pricing (mutually exclusive with fixed_price). Bids below this value will be rejected.', ge=0.0, ), ] = None min_spend_per_package: Annotated[ float | None, Field( description='Minimum spend requirement per package using this pricing option, in the specified currency', ge=0.0, ), ] = None price_guidance: Annotated[ price_guidance_1.PriceGuidance | None, Field(description='Optional pricing guidance for auction-based bidding'), ] = None pricing_model: Annotated[Literal['cpc'], Field(description='Cost per click')] pricing_option_id: Annotated[ str, Field(description='Unique identifier for this pricing option within the product') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 currency : strvar fixed_price : float | Nonevar floor_price : float | Nonevar min_spend_per_package : float | Nonevar model_configvar price_guidance : PriceGuidance | Nonevar pricing_model : Literal['cpc']var pricing_option_id : str
Inherited members
class CpcvPricingOption (**data: Any)-
Expand source code
class CpcvPricingOption(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) currency: Annotated[ str, Field( description='ISO 4217 currency code', examples=['USD', 'EUR', 'GBP', 'JPY'], pattern='^[A-Z]{3}$', ), ] fixed_price: Annotated[ float | None, Field( description='Fixed price per completed view. If present, this is fixed pricing. If absent, auction-based.', ge=0.0, ), ] = None floor_price: Annotated[ float | None, Field( description='Minimum acceptable bid for auction pricing (mutually exclusive with fixed_price). Bids below this value will be rejected.', ge=0.0, ), ] = None min_spend_per_package: Annotated[ float | None, Field( description='Minimum spend requirement per package using this pricing option, in the specified currency', ge=0.0, ), ] = None price_guidance: Annotated[ price_guidance_1.PriceGuidance | None, Field(description='Optional pricing guidance for auction-based bidding'), ] = None pricing_model: Annotated[ Literal['cpcv'], Field(description='Cost per completed view (100% completion)') ] pricing_option_id: Annotated[ str, Field(description='Unique identifier for this pricing option within the product') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 currency : strvar fixed_price : float | Nonevar floor_price : float | Nonevar min_spend_per_package : float | Nonevar model_configvar price_guidance : PriceGuidance | Nonevar pricing_model : Literal['cpcv']var pricing_option_id : str
Inherited members
class CpmPricingOption (**data: Any)-
Expand source code
class CpmPricingOption(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) currency: Annotated[ str, Field( description='ISO 4217 currency code', examples=['USD', 'EUR', 'GBP', 'JPY'], pattern='^[A-Z]{3}$', ), ] fixed_price: Annotated[ float | None, Field( description='Fixed price per unit. If present, this is fixed pricing. If absent, auction-based.', ge=0.0, ), ] = None floor_price: Annotated[ float | None, Field( description='Minimum acceptable bid for auction pricing (mutually exclusive with fixed_price). Bids below this value will be rejected.', ge=0.0, ), ] = None min_spend_per_package: Annotated[ float | None, Field( description='Minimum spend requirement per package using this pricing option, in the specified currency', ge=0.0, ), ] = None price_guidance: Annotated[ price_guidance_1.PriceGuidance | None, Field(description='Optional pricing guidance for auction-based bidding'), ] = None pricing_model: Annotated[Literal['cpm'], Field(description='Cost per 1,000 impressions')] pricing_option_id: Annotated[ str, Field(description='Unique identifier for this pricing option within the product') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 currency : strvar fixed_price : float | Nonevar floor_price : float | Nonevar min_spend_per_package : float | Nonevar model_configvar price_guidance : PriceGuidance | Nonevar pricing_model : Literal['cpm']var pricing_option_id : str
Inherited members
class CppPricingOption (**data: Any)-
Expand source code
class CppPricingOption(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) currency: Annotated[ str, Field( description='ISO 4217 currency code', examples=['USD', 'EUR', 'GBP', 'JPY'], pattern='^[A-Z]{3}$', ), ] fixed_price: Annotated[ float | None, Field( description='Fixed price per rating point. If present, this is fixed pricing. If absent, auction-based.', ge=0.0, ), ] = None floor_price: Annotated[ float | None, Field( description='Minimum acceptable bid for auction pricing (mutually exclusive with fixed_price). Bids below this value will be rejected.', ge=0.0, ), ] = None min_spend_per_package: Annotated[ float | None, Field( description='Minimum spend requirement per package using this pricing option, in the specified currency', ge=0.0, ), ] = None parameters: Annotated[ Parameters, Field(description='CPP-specific parameters for demographic targeting') ] price_guidance: Annotated[ price_guidance_1.PriceGuidance | None, Field(description='Optional pricing guidance for auction-based bidding'), ] = None pricing_model: Annotated[Literal['cpp'], Field(description='Cost per Gross Rating Point')] pricing_option_id: Annotated[ str, Field(description='Unique identifier for this pricing option within the product') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 currency : strvar fixed_price : float | Nonevar floor_price : float | Nonevar min_spend_per_package : float | Nonevar model_configvar parameters : Parametersvar price_guidance : PriceGuidance | Nonevar pricing_model : Literal['cpp']var pricing_option_id : str
Inherited members
class CpvPricingOption (**data: Any)-
Expand source code
class CpvPricingOption(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) currency: Annotated[ str, Field( description='ISO 4217 currency code', examples=['USD', 'EUR', 'GBP', 'JPY'], pattern='^[A-Z]{3}$', ), ] fixed_price: Annotated[ float | None, Field( description='Fixed price per view. If present, this is fixed pricing. If absent, auction-based.', ge=0.0, ), ] = None floor_price: Annotated[ float | None, Field( description='Minimum acceptable bid for auction pricing (mutually exclusive with fixed_price). Bids below this value will be rejected.', ge=0.0, ), ] = None min_spend_per_package: Annotated[ float | None, Field( description='Minimum spend requirement per package using this pricing option, in the specified currency', ge=0.0, ), ] = None parameters: Annotated[ Parameters, Field(description='CPV-specific parameters defining the view threshold') ] price_guidance: Annotated[ price_guidance_1.PriceGuidance | None, Field(description='Optional pricing guidance for auction-based bidding'), ] = None pricing_model: Annotated[Literal['cpv'], Field(description='Cost per view at threshold')] pricing_option_id: Annotated[ str, Field(description='Unique identifier for this pricing option within the product') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 currency : strvar fixed_price : float | Nonevar floor_price : float | Nonevar min_spend_per_package : float | Nonevar model_configvar parameters : Parametersvar price_guidance : PriceGuidance | Nonevar pricing_model : Literal['cpv']var pricing_option_id : str
Inherited members
class CreateContentStandardsSuccessResponse (**data: Any)-
Expand source code
class CreateContentStandardsResponse1(AdCPBaseModel): context: context_1.ContextObject | None = None errors: Annotated[ Any | None, Field(description='Field must not be present in success response') ] = None ext: ext_1.ExtensionObject | None = None standards_id: Annotated[ str, Field(description='Unique identifier for the created standards configuration') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : typing.Any | Nonevar ext : ExtensionObject | Nonevar model_configvar standards_id : str
Inherited members
class CreateContentStandardsErrorResponse (**data: Any)-
Expand source code
class CreateContentStandardsResponse2(AdCPBaseModel): conflicting_standards_id: Annotated[ str | None, Field( description='If the error is a scope conflict, the ID of the existing standards that conflict' ), ] = None context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None standards_id: Annotated[ Any | None, Field(description='Field must not be present in error response') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 conflicting_standards_id : str | Nonevar context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_configvar standards_id : typing.Any | None
Inherited members
class CreateMediaBuyRequest (**data: Any)-
Expand source code
class CreateMediaBuyRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account_id: Annotated[ str | None, Field( description="Account to bill for this media buy. Required when the agent has access to multiple accounts; when omitted, the seller uses the agent's sole account. The seller maps the agent's brand + operator to an account during sync_accounts; the agent passes that account_id here." ), ] = None artifact_webhook: Annotated[ ArtifactWebhook | None, Field( description='Optional webhook configuration for content artifact delivery. Used by governance agents to validate content adjacency. Seller pushes artifacts to this endpoint; orchestrator forwards to governance agent for validation.' ), ] = None brand_manifest: Annotated[ brand_manifest_ref.BrandManifestReference, Field( description='Brand information manifest serving as the namespace and identity for this media buy. Provides brand context, assets, and product catalog. Can be provided inline or as a URL reference to a hosted manifest. Can be cached and reused across multiple requests.' ), ] buyer_ref: Annotated[str, Field(description="Buyer's reference identifier for this media buy")] context: context_1.ContextObject | None = None end_time: Annotated[ AwareDatetime, Field(description='Campaign end date/time in ISO 8601 format') ] ext: ext_1.ExtensionObject | None = None packages: Annotated[ list[package_request.PackageRequest] | None, Field( description="Array of package configurations. Required when not using proposal_id. When executing a proposal, this can be omitted and packages will be derived from the proposal's allocations.", min_length=1, ), ] = None po_number: Annotated[str | None, Field(description='Purchase order number for tracking')] = None proposal_id: Annotated[ str | None, Field( description="ID of a proposal from get_products to execute. When provided with total_budget, the publisher converts the proposal's allocation percentages into packages automatically. Alternative to providing packages array." ), ] = None reporting_webhook: Annotated[ reporting_webhook_1.ReportingWebhook | None, Field(description='Optional webhook configuration for automated reporting delivery'), ] = None start_time: start_timing.StartTiming total_budget: Annotated[ TotalBudget | None, Field( description="Total budget for the media buy when executing a proposal. The publisher applies the proposal's allocation percentages to this amount to derive package budgets." ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar artifact_webhook : ArtifactWebhook | Nonevar brand_manifest : BrandManifestReferencevar buyer_ref : strvar context : ContextObject | Nonevar end_time : pydantic.types.AwareDatetimevar ext : ExtensionObject | Nonevar model_configvar packages : list[PackageRequest] | Nonevar po_number : str | Nonevar proposal_id : str | Nonevar reporting_webhook : ReportingWebhook | Nonevar start_time : StartTimingvar total_budget : TotalBudget | None
Inherited members
class CreateMediaBuyResponse (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class CreateMediaBuyResponse(RootModel[CreateMediaBuyResponse1 | CreateMediaBuyResponse2]): root: Annotated[ CreateMediaBuyResponse1 | CreateMediaBuyResponse2, Field( description='Response payload for create_media_buy task. Returns either complete success data OR error information, never both. This enforces atomic operation semantics - the media buy is either fully created or not created at all.', title='Create Media Buy Response', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[CreateMediaBuyResponse1, CreateMediaBuyResponse2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : CreateMediaBuyResponse1 | CreateMediaBuyResponse2
class CreateMediaBuySuccessResponse (**data: Any)-
Expand source code
class CreateMediaBuyResponse1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account: Annotated[ account_1.Account | None, Field( description='Account billed for this media buy. Includes advertiser, billing proxy (if any), and rate card applied.' ), ] = None buyer_ref: Annotated[str, Field(description="Buyer's reference identifier for this media buy")] context: context_1.ContextObject | None = None creative_deadline: Annotated[ AwareDatetime | None, Field(description='ISO 8601 timestamp for creative upload deadline') ] = None ext: ext_1.ExtensionObject | None = None media_buy_id: Annotated[ str, Field(description="Publisher's unique identifier for the created media buy") ] packages: Annotated[ list[package.Package], Field(description='Array of created packages with complete state information'), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : Account | Nonevar buyer_ref : strvar context : ContextObject | Nonevar creative_deadline : pydantic.types.AwareDatetime | Nonevar ext : ExtensionObject | Nonevar media_buy_id : strvar model_configvar packages : list[Package]
Inherited members
class CreateMediaBuyErrorResponse (**data: Any)-
Expand source code
class CreateMediaBuyResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error], Field(description='Array of errors explaining why the operation failed', min_length=1), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_config
Inherited members
class Creative (**data: Any)-
Expand source code
class Creative(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) creative_id: Annotated[str, Field(description='Creative identifier')] format_id: Annotated[ format_id_1.FormatId | None, Field(description='Format of this creative') ] = None media_buy_id: Annotated[ str | None, Field( description="Publisher's media buy identifier for this creative. Present when the request spanned multiple media buys, so the buyer can correlate each creative to its media buy." ), ] = None totals: Annotated[ delivery_metrics.DeliveryMetrics | None, Field(description='Aggregate delivery metrics across all variants of this creative'), ] = None variant_count: Annotated[ int | None, Field( description='Total number of variants for this creative. When max_variants was specified in the request, this may exceed the number of items in the variants array.', ge=0, ), ] = None variants: Annotated[ list[creative_variant.CreativeVariant], Field( description='Variant-level delivery breakdown. Each variant includes the rendered manifest and delivery metrics. For standard creatives, contains a single variant. For asset group optimization, one per combination. For generative creative, one per generated execution. Empty when a creative has no variants yet.' ), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 creative_id : strvar format_id : FormatId | Nonevar media_buy_id : str | Nonevar model_configvar totals : DeliveryMetrics | Nonevar variant_count : int | Nonevar variants : list[CreativeVariant]
Inherited members
class CreativeFilters (**data: Any)-
Expand source code
class CreativeFilters(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account_ids: Annotated[ list[str] | None, Field( description='Filter creatives by owning accounts. Useful for agencies managing multiple client accounts.', min_length=1, ), ] = None assigned_to_packages: Annotated[ list[str] | None, Field(description='Filter creatives assigned to any of these packages', min_length=1), ] = None buyer_refs: Annotated[ list[str] | None, Field( description='Filter creatives assigned to media buys with any of these buyer references', min_length=1, ), ] = None created_after: Annotated[ AwareDatetime | None, Field(description='Filter creatives created after this date (ISO 8601)'), ] = None created_before: Annotated[ AwareDatetime | None, Field(description='Filter creatives created before this date (ISO 8601)'), ] = None creative_ids: Annotated[ list[str] | None, Field(description='Filter by specific creative IDs', max_length=100, min_length=1), ] = None formats: Annotated[ list[str] | None, Field( description='Filter by creative format types (e.g., video, audio, display)', min_length=1, ), ] = None has_performance_data: Annotated[ bool | None, Field(description='Filter creatives that have performance data when true') ] = None media_buy_ids: Annotated[ list[str] | None, Field(description='Filter creatives assigned to any of these media buys', min_length=1), ] = None name_contains: Annotated[ str | None, Field(description='Filter by creative names containing this text (case-insensitive)'), ] = None statuses: Annotated[ list[creative_status.CreativeStatus] | None, Field(description='Filter by creative approval statuses', min_length=1), ] = None tags: Annotated[ list[str] | None, Field(description='Filter by creative tags (all tags must match)', min_length=1), ] = None tags_any: Annotated[ list[str] | None, Field(description='Filter by creative tags (any tag must match)', min_length=1), ] = None unassigned: Annotated[ bool | None, Field( description='Filter for unassigned creatives when true, assigned creatives when false' ), ] = None updated_after: Annotated[ AwareDatetime | None, Field(description='Filter creatives last updated after this date (ISO 8601)'), ] = None updated_before: Annotated[ AwareDatetime | None, Field(description='Filter creatives last updated before this date (ISO 8601)'), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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_ids : list[str] | Nonevar assigned_to_packages : list[str] | Nonevar buyer_refs : list[str] | Nonevar created_after : pydantic.types.AwareDatetime | Nonevar created_before : pydantic.types.AwareDatetime | Nonevar creative_ids : list[str] | Nonevar formats : list[str] | Nonevar has_performance_data : bool | Nonevar media_buy_ids : list[str] | Nonevar model_configvar name_contains : str | Nonevar statuses : list[CreativeStatus] | Nonevar unassigned : bool | Nonevar updated_after : pydantic.types.AwareDatetime | Nonevar updated_before : pydantic.types.AwareDatetime | None
Inherited members
class CreativeManifest (**data: Any)-
Expand source code
class CreativeManifest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) assets: Annotated[ dict[ str, image_asset.ImageAsset | video_asset.VideoAsset | audio_asset.AudioAsset | vast_asset.VastAsset | text_asset.TextAsset | url_asset.UrlAsset | html_asset.HtmlAsset | javascript_asset.JavascriptAsset | webhook_asset.WebhookAsset | css_asset.CssAsset | daast_asset.DaastAsset | promoted_offerings.PromotedOfferings, ], Field( description="Map of asset IDs to actual asset content. Each key MUST match an asset_id from the format's assets array (e.g., 'banner_image', 'clickthrough_url', 'video_file', 'vast_tag'). The asset_id is the technical identifier used to match assets to format requirements.\n\nIMPORTANT: Full validation requires format context. The format defines what type each asset_id should be. Standalone schema validation only checks structural conformance — each asset must match at least one valid asset type schema." ), ] ext: ext_1.ExtensionObject | None = None format_id: Annotated[ format_id_1.FormatId, Field( description="Format identifier this manifest is for. Can be a template format (id only) or a deterministic format (id + dimensions/duration). For dimension-specific creatives, include width/height/unit in the format_id to create a unique identifier (e.g., {id: 'display_static', width: 300, height: 250, unit: 'px'})." ), ] promoted_offering: Annotated[ str | None, Field( description='Product name or offering being advertised. Maps to promoted_offerings in create_media_buy request to associate creative with the product being promoted.' ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 assets : dict[str, ImageAsset | VideoAsset | AudioAsset | VastAsset | TextAsset | UrlAsset | HtmlAsset | JavascriptAsset | WebhookAsset | CssAsset | DaastAsset | PromotedOfferings]var ext : ExtensionObject | Nonevar format_id : FormatIdvar model_configvar promoted_offering : str | None
Inherited members
class CreativeStatus (*args, **kwds)-
Expand source code
class CreativeStatus(Enum): processing = 'processing' approved = 'approved' rejected = 'rejected' pending_review = 'pending_review' archived = 'archived'Create a collection of name/value pairs.
Example enumeration:
>>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3Access them by:
- attribute access::
>>> Color.RED <Color.RED: 1>- value lookup:
>>> Color(1) <Color.RED: 1>- name lookup:
>>> Color['RED'] <Color.RED: 1>Enumerations can be iterated over, and know how many members they have:
>>> len(Color) 3>>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.
Ancestors
- enum.Enum
Class variables
var approvedvar archivedvar pending_reviewvar processingvar rejected
class UrlDaastAsset (**data: Any)-
Expand source code
class DaastAsset1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) companion_ads: Annotated[ bool | None, Field(description='Whether companion display ads are included') ] = None daast_version: Annotated[ daast_version_1.DaastVersion | None, Field(description='DAAST specification version') ] = None delivery_type: Annotated[ Literal['url'], Field(description='Discriminator indicating DAAST is delivered via URL endpoint'), ] duration_ms: Annotated[ int | None, Field(description='Expected audio duration in milliseconds (if known)', ge=0) ] = None tracking_events: Annotated[ list[daast_tracking_event.DaastTrackingEvent] | None, Field(description='Tracking events supported by this DAAST tag'), ] = None transcript_url: Annotated[ AnyUrl | None, Field(description='URL to text transcript of the audio content') ] = None url: Annotated[AnyUrl, Field(description='URL endpoint that returns DAAST XML')]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 companion_ads : bool | Nonevar daast_version : DaastVersion | Nonevar delivery_type : Literal['url']var duration_ms : int | Nonevar model_configvar tracking_events : list[DaastTrackingEvent] | Nonevar transcript_url : pydantic.networks.AnyUrl | Nonevar url : pydantic.networks.AnyUrl
Inherited members
class InlineDaastAsset (**data: Any)-
Expand source code
class DaastAsset2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) companion_ads: Annotated[ bool | None, Field(description='Whether companion display ads are included') ] = None content: Annotated[str, Field(description='Inline DAAST XML content')] daast_version: Annotated[ daast_version_1.DaastVersion | None, Field(description='DAAST specification version') ] = None delivery_type: Annotated[ Literal['inline'], Field(description='Discriminator indicating DAAST is delivered as inline XML content'), ] duration_ms: Annotated[ int | None, Field(description='Expected audio duration in milliseconds (if known)', ge=0) ] = None tracking_events: Annotated[ list[daast_tracking_event.DaastTrackingEvent] | None, Field(description='Tracking events supported by this DAAST tag'), ] = None transcript_url: Annotated[ AnyUrl | None, Field(description='URL to text transcript of the audio content') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 companion_ads : bool | Nonevar content : strvar daast_version : DaastVersion | Nonevar delivery_type : Literal['inline']var duration_ms : int | Nonevar model_configvar tracking_events : list[DaastTrackingEvent] | Nonevar transcript_url : pydantic.networks.AnyUrl | None
Inherited members
class PlatformDeployment (**data: Any)-
Expand source code
class Deployment1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account: Annotated[str | None, Field(description='Account identifier if applicable')] = None activation_key: Annotated[ activation_key_1.ActivationKey | None, Field( description='The key to use for targeting. Only present if is_live=true AND requester has access to this deployment.' ), ] = None deployed_at: Annotated[ AwareDatetime | None, Field(description='Timestamp when activation completed (if is_live=true)'), ] = None estimated_activation_duration_minutes: Annotated[ float | None, Field( description='Estimated time to activate if not live, or to complete activation if in progress', ge=0.0, ), ] = None is_live: Annotated[ bool, Field(description='Whether signal is currently active on this deployment') ] platform: Annotated[str, Field(description='Platform identifier for DSPs')] type: Annotated[ Literal['platform'], Field(description='Discriminator indicating this is a platform-based deployment'), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar activation_key : ActivationKey | Nonevar deployed_at : pydantic.types.AwareDatetime | Nonevar estimated_activation_duration_minutes : float | Nonevar is_live : boolvar model_configvar platform : strvar type : Literal['platform']
Inherited members
class AgentDeployment (**data: Any)-
Expand source code
class Deployment2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account: Annotated[str | None, Field(description='Account identifier if applicable')] = None activation_key: Annotated[ activation_key_1.ActivationKey | None, Field( description='The key to use for targeting. Only present if is_live=true AND requester has access to this deployment.' ), ] = None agent_url: Annotated[AnyUrl, Field(description='URL identifying the deployment agent')] deployed_at: Annotated[ AwareDatetime | None, Field(description='Timestamp when activation completed (if is_live=true)'), ] = None estimated_activation_duration_minutes: Annotated[ float | None, Field( description='Estimated time to activate if not live, or to complete activation if in progress', ge=0.0, ), ] = None is_live: Annotated[ bool, Field(description='Whether signal is currently active on this deployment') ] type: Annotated[ Literal['agent'], Field(description='Discriminator indicating this is an agent URL-based deployment'), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar activation_key : ActivationKey | Nonevar agent_url : pydantic.networks.AnyUrlvar deployed_at : pydantic.types.AwareDatetime | Nonevar estimated_activation_duration_minutes : float | Nonevar is_live : boolvar model_configvar type : Literal['agent']
Inherited members
class PlatformDestination (**data: Any)-
Expand source code
class Destination1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account: Annotated[ str | None, Field(description='Optional account identifier on the platform') ] = None platform: Annotated[ str, Field(description="Platform identifier for DSPs (e.g., 'the-trade-desk', 'amazon-dsp')"), ] type: Annotated[ Literal['platform'], Field(description='Discriminator indicating this is a platform-based deployment'), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar model_configvar platform : strvar type : Literal['platform']
Inherited members
class AgentDestination (**data: Any)-
Expand source code
class Destination2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account: Annotated[ str | None, Field(description='Optional account identifier on the agent') ] = None agent_url: Annotated[ AnyUrl, Field(description='URL identifying the deployment agent (for sales agents, etc.)') ] type: Annotated[ Literal['agent'], Field(description='Discriminator indicating this is an agent URL-based deployment'), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar agent_url : pydantic.networks.AnyUrlvar model_configvar type : Literal['agent']
Inherited members
class Error (**data: Any)-
Expand source code
class Error(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) code: Annotated[str, Field(description='Error code for programmatic handling')] details: Annotated[ dict[str, Any] | None, Field(description='Additional task-specific error details') ] = None field: Annotated[ str | None, Field(description="Field path associated with the error (e.g., 'packages[0].targeting')"), ] = None message: Annotated[str, Field(description='Human-readable error message')] retry_after: Annotated[ float | None, Field(description='Seconds to wait before retrying the operation', ge=0.0) ] = None suggestion: Annotated[str | None, Field(description='Suggested fix for the error')] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 code : strvar details : dict[str, typing.Any] | Nonevar field : str | Nonevar message : strvar model_configvar retry_after : float | Nonevar suggestion : str | None
Inherited members
class FlatRatePricingOption (**data: Any)-
Expand source code
class FlatRatePricingOption(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) currency: Annotated[ str, Field( description='ISO 4217 currency code', examples=['USD', 'EUR', 'GBP', 'JPY'], pattern='^[A-Z]{3}$', ), ] fixed_price: Annotated[ float | None, Field( description='Flat rate cost. If present, this is fixed pricing. If absent, auction-based.', ge=0.0, ), ] = None floor_price: Annotated[ float | None, Field( description='Minimum acceptable bid for auction pricing (mutually exclusive with fixed_price). Bids below this value will be rejected.', ge=0.0, ), ] = None min_spend_per_package: Annotated[ float | None, Field( description='Minimum spend requirement per package using this pricing option, in the specified currency', ge=0.0, ), ] = None parameters: Annotated[ Parameters | None, Field(description='Flat rate parameters for DOOH and time-based campaigns'), ] = None price_guidance: Annotated[ price_guidance_1.PriceGuidance | None, Field(description='Optional pricing guidance for auction-based bidding'), ] = None pricing_model: Annotated[ Literal['flat_rate'], Field(description='Fixed cost regardless of delivery volume') ] pricing_option_id: Annotated[ str, Field(description='Unique identifier for this pricing option within the product') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 currency : strvar fixed_price : float | Nonevar floor_price : float | Nonevar min_spend_per_package : float | Nonevar model_configvar parameters : Parameters | Nonevar price_guidance : PriceGuidance | Nonevar pricing_model : Literal['flat_rate']var pricing_option_id : str
Inherited members
class Format (**data: Any)-
Expand source code
class Format(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) accepts_parameters: Annotated[ list[format_id_parameter.FormatIdParameter] | None, Field( description='List of parameters this format accepts in format_id. Template formats define which parameters (dimensions, duration, etc.) can be specified when instantiating the format. Empty or omitted means this is a concrete format with fixed parameters.' ), ] = None accessibility: Annotated[ Accessibility | None, Field( description='Accessibility posture of this format. Declares the WCAG conformance level that creatives produced by this format will meet.' ), ] = None assets: Annotated[ list[ Assets | Assets5 | Assets6 | Assets7 | Assets8 | Assets9 | Assets10 | Assets11 | Assets12 | Assets13 | Assets14 | Assets15 | Assets16 | Assets17 ] | None, Field( description="Array of all assets supported for this format. Each asset is identified by its asset_id, which must be used as the key in creative manifests. Use the 'required' boolean on each asset to indicate whether it's mandatory." ), ] = None delivery: Annotated[ dict[str, Any] | None, Field(description='Delivery method specifications (e.g., hosted, VAST, third-party tags)'), ] = None description: Annotated[ str | None, Field( description='Plain text explanation of what this format does and what assets it requires' ), ] = None example_url: Annotated[ AnyUrl | None, Field( description='Optional URL to showcase page with examples and interactive demos of this format' ), ] = None format_card: Annotated[ FormatCard | None, Field( description='Optional standard visual card (300x400px) for displaying this format in user interfaces. Can be rendered via preview_creative or pre-generated.' ), ] = None format_card_detailed: Annotated[ FormatCardDetailed | None, Field( description='Optional detailed card with carousel and full specifications. Provides rich format documentation similar to ad spec pages.' ), ] = None format_id: Annotated[ format_id_1.FormatId, Field(description='Structured format identifier with agent URL and format name'), ] name: Annotated[str, Field(description='Human-readable format name')] output_format_ids: Annotated[ list[format_id_1.FormatId] | None, Field( description='For generative formats: array of format IDs that this format can generate. When a format accepts inputs like brand_manifest and message, this specifies what concrete output formats can be produced (e.g., a generative banner format might output standard image banner formats).' ), ] = None renders: Annotated[ list[Renders | Renders1] | None, Field( description='Specification of rendered pieces for this format. Most formats produce a single render. Companion ad formats (video + banner), adaptive formats, and multi-placement formats produce multiple renders. Each render specifies its role and dimensions.', min_length=1, ), ] = None reported_metrics: Annotated[ list[available_metric.AvailableMetric] | None, Field( description='Metrics this format can produce in delivery reporting. Buyers receive the intersection of format reported_metrics and product available_metrics. If omitted, the format defers entirely to product-level metric declarations.', min_length=1, ), ] = None supported_macros: Annotated[ list[universal_macro.UniversalMacro | str] | None, Field( description='List of universal macros supported by this format (e.g., MEDIA_BUY_ID, CACHEBUSTER, DEVICE_ID). Used for validation and developer tooling. See docs/creative/universal-macros.mdx for full documentation.' ), ] = None type: Annotated[ format_category.FormatCategory | None, Field( description='DEPRECATED: High-level category for this format. Use the assets array to understand creative requirements instead - it provides precise information about what asset types are needed.' ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 accepts_parameters : list[FormatIdParameter] | Nonevar accessibility : Accessibility | Nonevar assets : list[Assets | Assets5 | Assets6 | Assets7 | Assets8 | Assets9 | Assets10 | Assets11 | Assets12 | Assets13 | Assets14 | Assets15 | Assets16 | Assets17] | Nonevar delivery : dict[str, typing.Any] | Nonevar description : str | Nonevar example_url : pydantic.networks.AnyUrl | Nonevar format_card : FormatCard | Nonevar format_card_detailed : FormatCardDetailed | Nonevar format_id : FormatIdvar model_configvar name : strvar output_format_ids : list[FormatId] | Nonevar renders : list[Renders | Renders1] | Nonevar reported_metrics : list[AvailableMetric] | Nonevar supported_macros : list[UniversalMacro | str] | Nonevar type : FormatCategory | None
Inherited members
class FormatCategory (*args, **kwds)-
Expand source code
class FormatCategory(Enum): audio = 'audio' video = 'video' display = 'display' native = 'native' dooh = 'dooh' rich_media = 'rich_media' universal = 'universal'Create a collection of name/value pairs.
Example enumeration:
>>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3Access them by:
- attribute access::
>>> Color.RED <Color.RED: 1>- value lookup:
>>> Color(1) <Color.RED: 1>- name lookup:
>>> Color['RED'] <Color.RED: 1>Enumerations can be iterated over, and know how many members they have:
>>> len(Color) 3>>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.
Ancestors
- enum.Enum
Class variables
var audiovar displayvar doohvar nativevar rich_mediavar universalvar video
class FormatId (**data: Any)-
Expand source code
class FormatId(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) agent_url: Annotated[ AnyUrl, Field( description="URL of the agent that defines this format (e.g., 'https://creatives.adcontextprotocol.org' for standard formats, or 'https://publisher.com/.well-known/adcp/sales' for custom formats)" ), ] duration_ms: Annotated[ float | None, Field( description='Duration in milliseconds for time-based formats (video, audio). When specified, creates a parameterized format ID. Omit to reference a template format without parameters.', ge=1.0, ), ] = None height: Annotated[ int | None, Field( description='Height in pixels for visual formats. When specified, width must also be specified. Both fields together create a parameterized format ID for dimension-specific variants.', ge=1, ), ] = None id: Annotated[ str, Field( description="Format identifier within the agent's namespace (e.g., 'display_static', 'video_hosted', 'audio_standard'). When used alone, references a template format. When combined with dimension/duration fields, creates a parameterized format ID for a specific variant.", pattern='^[a-zA-Z0-9_-]+$', ), ] width: Annotated[ int | None, Field( description='Width in pixels for visual formats. When specified, height must also be specified. Both fields together create a parameterized format ID for dimension-specific variants.', ge=1, ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 agent_url : pydantic.networks.AnyUrlvar duration_ms : float | Nonevar height : int | Nonevar id : strvar model_configvar width : int | None
Inherited members
class GetContentStandardsSuccessResponse (**data: Any)-
Expand source code
class GetContentStandardsResponse1(ContentStandards): context: context_1.ContextObject | None = None errors: Annotated[ Any | None, Field(description='Field must not be present in success response') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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
- ContentStandards
- AdCPBaseModel
- pydantic.main.BaseModel
Class variables
var context : ContextObject | Nonevar errors : typing.Any | Nonevar model_config
Inherited members
class GetContentStandardsErrorResponse (**data: Any)-
Expand source code
class GetContentStandardsResponse2(AdCPBaseModel): context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None standards_id: Annotated[ Any | None, Field(description='Field must not be present in error response') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_configvar standards_id : typing.Any | None
Inherited members
class GetCreativeDeliveryRequest (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class GetCreativeDeliveryRequest( RootModel[ GetCreativeDeliveryRequest1 | GetCreativeDeliveryRequest2 | GetCreativeDeliveryRequest3 ] ): root: Annotated[ GetCreativeDeliveryRequest1 | GetCreativeDeliveryRequest2 | GetCreativeDeliveryRequest3, Field( description='Request parameters for retrieving creative delivery data including variant-level metrics from a creative agent. At least one scoping filter (media_buy_ids, media_buy_buyer_refs, or creative_ids) is required.', title='Get Creative Delivery Request', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[GetCreativeDeliveryRequest1, GetCreativeDeliveryRequest2, GetCreativeDeliveryRequest3]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : GetCreativeDeliveryRequest1 | GetCreativeDeliveryRequest2 | GetCreativeDeliveryRequest3
class GetCreativeDeliveryByMediaBuyRequest (**data: Any)-
Expand source code
class GetCreativeDeliveryRequest1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account_id: Annotated[ str | None, Field( description='Account context for routing and scoping. Limits results to creatives within this account. Optional if the agent has a single account or can determine routing from the media buy identifiers.' ), ] = None context: context_1.ContextObject | None = None creative_ids: Annotated[ list[str] | None, Field( description='Filter to specific creatives by ID. If omitted, returns delivery for all creatives matching the other filters.', min_length=1, ), ] = None end_date: Annotated[ str | None, Field( description="End date for delivery period (YYYY-MM-DD). Interpreted in the platform's reporting timezone.", pattern='^\\d{4}-\\d{2}-\\d{2}$', ), ] = None ext: ext_1.ExtensionObject | None = None max_variants: Annotated[ int | None, Field( description='Maximum number of variants to return per creative. When omitted, the agent returns all variants. Use this to limit response size for generative creatives that may produce large numbers of variants.', ge=1, ), ] = None media_buy_buyer_refs: Annotated[ list[str] | None, Field( description="Filter to specific media buys by buyer reference ID. Alternative to media_buy_ids when the buyer doesn't have the publisher's identifiers.", min_length=1, ), ] = None media_buy_ids: Annotated[ list[str], Field( description='Filter to specific media buys by publisher ID. If omitted, returns creative delivery across all matching media buys.', min_length=1, ), ] pagination: Annotated[ Pagination | None, Field( description='Pagination parameters for the creatives array in the response. When omitted, the agent returns all matching creatives.' ), ] = None start_date: Annotated[ str | None, Field( description="Start date for delivery period (YYYY-MM-DD). Interpreted in the platform's reporting timezone.", pattern='^\\d{4}-\\d{2}-\\d{2}$', ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar context : ContextObject | Nonevar creative_ids : list[str] | Nonevar end_date : str | Nonevar ext : ExtensionObject | Nonevar max_variants : int | Nonevar media_buy_buyer_refs : list[str] | Nonevar media_buy_ids : list[str]var model_configvar pagination : Pagination | Nonevar start_date : str | None
Inherited members
class GetCreativeDeliveryByBuyerRefRequest (**data: Any)-
Expand source code
class GetCreativeDeliveryRequest2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account_id: Annotated[ str | None, Field( description='Account context for routing and scoping. Limits results to creatives within this account. Optional if the agent has a single account or can determine routing from the media buy identifiers.' ), ] = None context: context_1.ContextObject | None = None creative_ids: Annotated[ list[str] | None, Field( description='Filter to specific creatives by ID. If omitted, returns delivery for all creatives matching the other filters.', min_length=1, ), ] = None end_date: Annotated[ str | None, Field( description="End date for delivery period (YYYY-MM-DD). Interpreted in the platform's reporting timezone.", pattern='^\\d{4}-\\d{2}-\\d{2}$', ), ] = None ext: ext_1.ExtensionObject | None = None max_variants: Annotated[ int | None, Field( description='Maximum number of variants to return per creative. When omitted, the agent returns all variants. Use this to limit response size for generative creatives that may produce large numbers of variants.', ge=1, ), ] = None media_buy_buyer_refs: Annotated[ list[str], Field( description="Filter to specific media buys by buyer reference ID. Alternative to media_buy_ids when the buyer doesn't have the publisher's identifiers.", min_length=1, ), ] media_buy_ids: Annotated[ list[str] | None, Field( description='Filter to specific media buys by publisher ID. If omitted, returns creative delivery across all matching media buys.', min_length=1, ), ] = None pagination: Annotated[ Pagination | None, Field( description='Pagination parameters for the creatives array in the response. When omitted, the agent returns all matching creatives.' ), ] = None start_date: Annotated[ str | None, Field( description="Start date for delivery period (YYYY-MM-DD). Interpreted in the platform's reporting timezone.", pattern='^\\d{4}-\\d{2}-\\d{2}$', ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar context : ContextObject | Nonevar creative_ids : list[str] | Nonevar end_date : str | Nonevar ext : ExtensionObject | Nonevar max_variants : int | Nonevar media_buy_buyer_refs : list[str]var media_buy_ids : list[str] | Nonevar model_configvar pagination : Pagination | Nonevar start_date : str | None
Inherited members
class GetCreativeDeliveryByCreativeRequest (**data: Any)-
Expand source code
class GetCreativeDeliveryRequest3(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account_id: Annotated[ str | None, Field( description='Account context for routing and scoping. Limits results to creatives within this account. Optional if the agent has a single account or can determine routing from the media buy identifiers.' ), ] = None context: context_1.ContextObject | None = None creative_ids: Annotated[ list[str], Field( description='Filter to specific creatives by ID. If omitted, returns delivery for all creatives matching the other filters.', min_length=1, ), ] end_date: Annotated[ str | None, Field( description="End date for delivery period (YYYY-MM-DD). Interpreted in the platform's reporting timezone.", pattern='^\\d{4}-\\d{2}-\\d{2}$', ), ] = None ext: ext_1.ExtensionObject | None = None max_variants: Annotated[ int | None, Field( description='Maximum number of variants to return per creative. When omitted, the agent returns all variants. Use this to limit response size for generative creatives that may produce large numbers of variants.', ge=1, ), ] = None media_buy_buyer_refs: Annotated[ list[str] | None, Field( description="Filter to specific media buys by buyer reference ID. Alternative to media_buy_ids when the buyer doesn't have the publisher's identifiers.", min_length=1, ), ] = None media_buy_ids: Annotated[ list[str] | None, Field( description='Filter to specific media buys by publisher ID. If omitted, returns creative delivery across all matching media buys.', min_length=1, ), ] = None pagination: Annotated[ Pagination | None, Field( description='Pagination parameters for the creatives array in the response. When omitted, the agent returns all matching creatives.' ), ] = None start_date: Annotated[ str | None, Field( description="Start date for delivery period (YYYY-MM-DD). Interpreted in the platform's reporting timezone.", pattern='^\\d{4}-\\d{2}-\\d{2}$', ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar context : ContextObject | Nonevar creative_ids : list[str]var end_date : str | Nonevar ext : ExtensionObject | Nonevar max_variants : int | Nonevar media_buy_buyer_refs : list[str] | Nonevar media_buy_ids : list[str] | Nonevar model_configvar pagination : Pagination | Nonevar start_date : str | None
Inherited members
class GetCreativeDeliveryResponse (**data: Any)-
Expand source code
class GetCreativeDeliveryResponse(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account_id: Annotated[ str | None, Field( description='Account identifier. Present when the response spans or is scoped to a specific account.' ), ] = None context: context_1.ContextObject | None = None creatives: Annotated[ list[Creative], Field(description='Creative delivery data with variant breakdowns') ] currency: Annotated[ str, Field( description="ISO 4217 currency code for monetary values in this response (e.g., 'USD', 'EUR')", pattern='^[A-Z]{3}$', ), ] errors: Annotated[ list[error.Error] | None, Field(description='Task-specific errors and warnings') ] = None ext: ext_1.ExtensionObject | None = None media_buy_buyer_ref: Annotated[ str | None, Field( description="Buyer's reference identifier for the media buy. Echoed back so the buyer can correlate without mapping publisher IDs." ), ] = None media_buy_id: Annotated[ str | None, Field( description="Publisher's media buy identifier. Present when the request was scoped to a single media buy." ), ] = None pagination: Annotated[ Pagination | None, Field( description='Pagination information. Present when the request included pagination parameters.' ), ] = None reporting_period: Annotated[ReportingPeriod, Field(description='Date range for the report.')]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar context : ContextObject | Nonevar creatives : list[Creative]var currency : strvar errors : list[Error] | Nonevar ext : ExtensionObject | Nonevar media_buy_buyer_ref : str | Nonevar media_buy_id : str | Nonevar model_configvar pagination : Pagination | Nonevar reporting_period : ReportingPeriod
Inherited members
class GetMediaBuyArtifactsSuccessResponse (**data: Any)-
Expand source code
class GetMediaBuyArtifactsResponse1(AdCPBaseModel): artifacts: Annotated[ list[Artifact], Field(description='Delivery records with full artifact content') ] context: context_1.ContextObject | None = None errors: Annotated[ Any | None, Field(description='Field must not be present in success response') ] = None ext: ext_1.ExtensionObject | None = None media_buy_id: Annotated[str, Field(description='Media buy these artifacts belong to')] pagination: pagination_response.PaginationResponse | None = None sampling_info: Annotated[ SamplingInfo | None, Field(description='Information about how the sample was generated') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 artifacts : list[Artifact]var context : ContextObject | Nonevar errors : typing.Any | Nonevar ext : ExtensionObject | Nonevar media_buy_id : strvar model_configvar pagination : PaginationResponse | Nonevar sampling_info : SamplingInfo | None
Inherited members
class GetMediaBuyArtifactsErrorResponse (**data: Any)-
Expand source code
class GetMediaBuyArtifactsResponse2(AdCPBaseModel): context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None media_buy_id: Annotated[ Any | None, Field(description='Field must not be present in error response') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar media_buy_id : typing.Any | Nonevar model_config
Inherited members
class GetMediaBuyDeliveryRequest (**data: Any)-
Expand source code
class GetMediaBuyDeliveryRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account_id: Annotated[ str | None, Field( description='Filter delivery data to a specific account. When provided, only returns media buys belonging to this account. When omitted, returns data across all accessible accounts. Optional if the agent has a single account.' ), ] = None buyer_refs: Annotated[ list[str] | None, Field(description='Array of buyer reference IDs to get delivery data for', min_length=1), ] = None context: context_1.ContextObject | None = None end_date: Annotated[ str | None, Field( description="End date for reporting period (YYYY-MM-DD). 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 ext: ext_1.ExtensionObject | None = None media_buy_ids: Annotated[ list[str] | None, Field( description='Array of publisher media buy IDs to get delivery data for', min_length=1 ), ] = None start_date: Annotated[ str | None, Field( description="Start date for reporting period (YYYY-MM-DD). When omitted along with end_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 status_filter: Annotated[ media_buy_status.MediaBuyStatus | StatusFilter | None, Field(description='Filter by status. Can be a single status or array of statuses'), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar buyer_refs : list[str] | Nonevar context : ContextObject | Nonevar end_date : str | Nonevar ext : ExtensionObject | Nonevar media_buy_ids : list[str] | Nonevar model_configvar start_date : str | Nonevar status_filter : MediaBuyStatus | StatusFilter | None
Inherited members
class GetMediaBuyDeliveryResponse (**data: Any)-
Expand source code
class GetMediaBuyDeliveryResponse(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) aggregated_totals: Annotated[ AggregatedTotals | None, Field( description='Combined metrics across all returned media buys. Only included in API responses (get_media_buy_delivery), not in webhook notifications.' ), ] = None attribution_window: Annotated[ attribution_window_1.AttributionWindow | None, Field( description='Attribution methodology and lookback windows used for conversion metrics in this response. All media buys from a single seller share the same attribution methodology. Enables cross-platform comparison (e.g., Amazon 14-day click vs. Criteo 30-day click).' ), ] = None context: context_1.ContextObject | None = None currency: Annotated[str, Field(description='ISO 4217 currency code', pattern='^[A-Z]{3}$')] errors: Annotated[ list[error.Error] | None, Field( description='Task-specific errors and warnings (e.g., missing delivery data, reporting platform issues)' ), ] = None ext: ext_1.ExtensionObject | None = None media_buy_deliveries: Annotated[ list[MediaBuyDelivery], Field( description='Array of delivery data for media buys. When used in webhook notifications, may contain multiple media buys aggregated by publisher. When used in get_media_buy_delivery API responses, typically contains requested media buys.' ), ] next_expected_at: Annotated[ AwareDatetime | None, Field( description="ISO 8601 timestamp for next expected notification (only present in webhook deliveries when notification_type is not 'final')" ), ] = None notification_type: Annotated[ NotificationType | None, Field( description='Type of webhook notification (only present in webhook deliveries): scheduled = regular periodic update, final = campaign completed, delayed = data not yet available, adjusted = resending period with updated data' ), ] = None partial_data: Annotated[ bool | None, Field( description='Indicates if any media buys in this webhook have missing/delayed data (only present in webhook deliveries)' ), ] = None reporting_period: Annotated[ ReportingPeriod, Field(description='Date range for the report. All periods use UTC timezone.'), ] sequence_number: Annotated[ int | None, Field( description='Sequential notification number (only present in webhook deliveries, starts at 1)', ge=1, ), ] = None unavailable_count: Annotated[ int | None, Field( description='Number of media buys with reporting_delayed or failed status (only present in webhook deliveries when partial_data is true)', ge=0, ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 aggregated_totals : AggregatedTotals | Nonevar attribution_window : AttributionWindow | Nonevar context : ContextObject | Nonevar currency : strvar errors : list[Error] | Nonevar ext : ExtensionObject | Nonevar media_buy_deliveries : list[MediaBuyDelivery]var model_configvar next_expected_at : pydantic.types.AwareDatetime | Nonevar notification_type : NotificationType | Nonevar partial_data : bool | Nonevar reporting_period : ReportingPeriodvar sequence_number : int | None
Inherited members
class GetProductsRequest (**data: Any)-
Expand source code
class GetProductsRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account_id: Annotated[ str | None, Field( description="Account ID for product lookup. Required when the seller declares account.required_for_products = true in capabilities. Returns products with pricing specific to this account's rate card." ), ] = None brand_manifest: Annotated[ brand_manifest_ref.BrandManifestReference | None, Field( description='Brand information manifest providing brand context, assets, and product catalog. Can be provided inline or as a URL reference to a hosted manifest.' ), ] = None brief: Annotated[ str | None, Field( description="Natural language description of campaign requirements. When refining a proposal, can include instructions like 'focus more on German speakers' or 'increase mobile allocation'." ), ] = None context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None filters: product_filters.ProductFilters | None = None pagination: pagination_request.PaginationRequest | None = None product_selectors: Annotated[ promoted_products.PromotedProducts | None, Field( description='Selectors to filter the brand manifest product catalog for product discovery. When provided, sellers should only return advertising products where the selected catalog items have matches. Uses the same selection methods as promoted-offerings.' ), ] = None property_list: Annotated[ property_list_ref.PropertyListReference | None, Field( description='[AdCP 3.0] Reference to an externally managed property list. When provided, the sales agent should filter products to only those available on properties in the list.' ), ] = None proposal_id: Annotated[ str | None, Field( description='Optional proposal ID to refine. When provided with a brief, the publisher will use the brief as refinement instructions for the specified proposal and return an updated version.' ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar brand_manifest : BrandManifestReference | Nonevar brief : str | Nonevar context : ContextObject | Nonevar ext : ExtensionObject | Nonevar filters : ProductFilters | Nonevar model_configvar pagination : PaginationRequest | Nonevar product_selectors : PromotedProducts | Nonevar property_list : PropertyListReference | Nonevar proposal_id : str | None
Inherited members
class GetProductsResponse (**data: Any)-
Expand source code
class GetProductsResponse(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error] | None, Field(description='Task-specific errors and warnings (e.g., product filtering issues)'), ] = None ext: ext_1.ExtensionObject | None = None pagination: pagination_response.PaginationResponse | None = None product_selectors_applied: Annotated[ bool | None, Field( description='Indicates whether product_selectors filtering was applied. True if the seller filtered results based on the provided product_selectors. Absent or false if product_selectors was not provided or not supported by this agent.' ), ] = None products: Annotated[list[product.Product], Field(description='Array of matching products')] property_list_applied: Annotated[ bool | None, Field( description='[AdCP 3.0] Indicates whether property_list filtering was applied. True if the agent filtered products based on the provided property_list. Absent or false if property_list was not provided or not supported by this agent.' ), ] = None proposals: Annotated[ list[proposal.Proposal] | None, Field( description='Optional array of proposed media plans with budget allocations across products. Publishers include proposals when they can provide strategic guidance based on the brief. Proposals are actionable - buyers can refine them via subsequent get_products calls or execute them directly via create_media_buy.' ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error] | Nonevar ext : ExtensionObject | Nonevar model_configvar pagination : PaginationResponse | Nonevar product_selectors_applied : bool | Nonevar products : list[Product]var property_list_applied : bool | Nonevar proposals : list[Proposal] | None
Inherited members
class GetSignalsRequest (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class GetSignalsRequest(RootModel[GetSignalsRequest1 | GetSignalsRequest2]): root: Annotated[ GetSignalsRequest1 | GetSignalsRequest2, Field( description='Request parameters for discovering signals. Use signal_spec for natural language discovery, signal_ids for exact lookups, or both (signal_ids take precedence for exact matches, signal_spec provides additional discovery context).', title='Get Signals Request', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[GetSignalsRequest1, GetSignalsRequest2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : GetSignalsRequest1 | GetSignalsRequest2
class GetSignalsResponse (**data: Any)-
Expand source code
class GetSignalsResponse(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error] | None, Field( description='Task-specific errors and warnings (e.g., signal discovery or pricing issues)' ), ] = None ext: ext_1.ExtensionObject | None = None pagination: pagination_response.PaginationResponse | None = None signals: Annotated[list[Signal], Field(description='Array of matching signals')]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error] | Nonevar ext : ExtensionObject | Nonevar model_configvar pagination : PaginationResponse | Nonevar signals : list[Signal]
Inherited members
class ListAccountsRequest (**data: Any)-
Expand source code
class ListAccountsRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None pagination: pagination_request.PaginationRequest | None = None status: Annotated[ Status | None, Field(description='Filter accounts by status. Omit to return accounts in all statuses.'), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar ext : ExtensionObject | Nonevar model_configvar pagination : PaginationRequest | Nonevar status : Status | None
Inherited members
class ListAccountsResponse (**data: Any)-
Expand source code
class ListAccountsResponse(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) accounts: Annotated[ list[account.Account], Field(description='Array of accounts accessible to the authenticated agent'), ] context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error] | None, Field(description='Task-specific errors and warnings') ] = None ext: ext_1.ExtensionObject | None = None pagination: pagination_response.PaginationResponse | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 accounts : list[Account]var context : ContextObject | Nonevar errors : list[Error] | Nonevar ext : ExtensionObject | Nonevar model_configvar pagination : PaginationResponse | None
Inherited members
class ListContentStandardsSuccessResponse (**data: Any)-
Expand source code
class ListContentStandardsResponse1(AdCPBaseModel): context: context_1.ContextObject | None = None errors: Annotated[ Any | None, Field(description='Field must not be present in success response') ] = None ext: ext_1.ExtensionObject | None = None pagination: pagination_response.PaginationResponse | None = None standards: Annotated[ list[content_standards.ContentStandards], Field(description='Array of content standards configurations matching the filter criteria'), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : typing.Any | Nonevar ext : ExtensionObject | Nonevar model_configvar pagination : PaginationResponse | Nonevar standards : list[ContentStandards]
Inherited members
class ListContentStandardsErrorResponse (**data: Any)-
Expand source code
class ListContentStandardsResponse2(AdCPBaseModel): context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None standards: Annotated[ Any | None, Field(description='Field must not be present in error response') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_configvar standards : typing.Any | None
Inherited members
class ListCreativeFormatsRequest (**data: Any)-
Expand source code
class ListCreativeFormatsRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) asset_types: Annotated[ list[asset_content_type.AssetContentType] | None, Field( description="Filter to formats that include these asset types. For third-party tags, search for 'html' or 'javascript'. E.g., ['image', 'text'] returns formats with images and text, ['javascript'] returns formats accepting JavaScript tags.", min_length=1, ), ] = None context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None format_ids: Annotated[ list[format_id.FormatId] | None, Field( description='Return only these specific format IDs (e.g., from get_products response)', min_length=1, ), ] = None is_responsive: Annotated[ bool | None, Field( description='Filter for responsive formats that adapt to container size. When true, returns formats without fixed dimensions.' ), ] = None max_height: Annotated[ int | None, Field( description='Maximum height in pixels (inclusive). Returns formats where ANY render has height <= this value. For multi-render formats, matches if at least one render fits.' ), ] = None max_width: Annotated[ int | None, Field( description='Maximum width in pixels (inclusive). Returns formats where ANY render has width <= this value. For multi-render formats, matches if at least one render fits.' ), ] = None min_height: Annotated[ int | None, Field( description='Minimum height in pixels (inclusive). Returns formats where ANY render has height >= this value.' ), ] = None min_width: Annotated[ int | None, Field( description='Minimum width in pixels (inclusive). Returns formats where ANY render has width >= this value.' ), ] = None name_search: Annotated[ str | None, Field(description='Search for formats by name (case-insensitive partial match)') ] = None pagination: pagination_request.PaginationRequest | None = None type: Annotated[ format_category.FormatCategory | None, Field( description='Filter by format type (technical categories with distinct requirements)' ), ] = None wcag_level: Annotated[ wcag_level_1.WcagLevel | None, Field( description='Filter to formats that meet at least this WCAG conformance level (A < AA < AAA)' ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 asset_types : list[AssetContentType] | Nonevar context : ContextObject | Nonevar ext : ExtensionObject | Nonevar format_ids : list[FormatId] | Nonevar is_responsive : bool | Nonevar max_height : int | Nonevar max_width : int | Nonevar min_height : int | Nonevar min_width : int | Nonevar model_configvar name_search : str | Nonevar pagination : PaginationRequest | Nonevar type : FormatCategory | Nonevar wcag_level : WcagLevel | None
Inherited members
class ListCreativeFormatsResponse (**data: Any)-
Expand source code
class ListCreativeFormatsResponse(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None creative_agents: Annotated[ list[CreativeAgent] | None, Field( description='Optional: Creative agents that provide additional formats. Buyers can recursively query these agents to discover more formats. No authentication required for list_creative_formats.' ), ] = None errors: Annotated[ list[error.Error] | None, Field(description='Task-specific errors and warnings (e.g., format availability issues)'), ] = None ext: ext_1.ExtensionObject | None = None formats: Annotated[ list[format.Format], Field( description="Full format definitions for all formats this agent supports. Each format's authoritative source is indicated by its agent_url field." ), ] pagination: pagination_response.PaginationResponse | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar creative_agents : list[CreativeAgent] | Nonevar errors : list[Error] | Nonevar ext : ExtensionObject | Nonevar formats : list[Format]var model_configvar pagination : PaginationResponse | None
Inherited members
class ListCreativesRequest (**data: Any)-
Expand source code
class ListCreativesRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None fields: Annotated[ list[FieldModel] | None, Field( description='Specific fields to include in response (omit for all fields)', min_length=1 ), ] = None filters: creative_filters.CreativeFilters | None = None include_assignments: Annotated[ bool | None, Field(description='Include package assignment information in response') ] = True include_performance: Annotated[ bool | None, Field(description='Include aggregated performance metrics in response') ] = False include_sub_assets: Annotated[ bool | None, Field(description='Include sub-assets (for carousel/native formats) in response'), ] = False pagination: pagination_request.PaginationRequest | None = None sort: Annotated[Sort | None, Field(description='Sorting parameters')] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar ext : ExtensionObject | Nonevar fields : list[FieldModel] | Nonevar filters : CreativeFilters | Nonevar include_assignments : bool | Nonevar include_performance : bool | Nonevar include_sub_assets : bool | Nonevar model_configvar pagination : PaginationRequest | Nonevar sort : Sort | None
Inherited members
class ListCreativesResponse (**data: Any)-
Expand source code
class ListCreativesResponse(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None creatives: Annotated[ list[Creative], Field(description='Array of creative assets matching the query') ] ext: ext_1.ExtensionObject | None = None format_summary: Annotated[ dict[str, int] | None, Field(description='Breakdown of creatives by format type') ] = None pagination: pagination_response.PaginationResponse query_summary: Annotated[ QuerySummary, Field(description='Summary of the query that was executed') ] status_summary: Annotated[ StatusSummary | None, Field(description='Breakdown of creatives by status') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar creatives : list[Creative]var ext : ExtensionObject | Nonevar format_summary : dict[str, int] | Nonevar model_configvar pagination : PaginationResponsevar query_summary : QuerySummaryvar status_summary : StatusSummary | None
Inherited members
class LogEventRequest (**data: Any)-
Expand source code
class LogEventRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None event_source_id: Annotated[ str, Field(description='Event source configured on the account via sync_event_sources') ] events: Annotated[ list[event.Event], Field(description='Events to log', max_length=10000, min_length=1) ] ext: ext_1.ExtensionObject | None = None test_event_code: Annotated[ str | None, Field( description="Test event code for validation without affecting production data. Events with this code appear in the platform's test events UI." ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar event_source_id : strvar events : list[Event]var ext : ExtensionObject | Nonevar model_configvar test_event_code : str | None
Inherited members
class LogEventResponse (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class LogEventResponse(RootModel[LogEventResponse1 | LogEventResponse2]): root: Annotated[ LogEventResponse1 | LogEventResponse2, Field( description='Response from event logging operation. Returns either event processing results OR operation-level errors.', title='Log Event Response', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[LogEventResponse1, LogEventResponse2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : LogEventResponse1 | LogEventResponse2
class LogEventSuccessResponse (**data: Any)-
Expand source code
class LogEventResponse1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None events_processed: Annotated[ int, Field(description='Number of events successfully queued for processing', ge=0) ] events_received: Annotated[int, Field(description='Number of events received', ge=0)] ext: ext_1.ExtensionObject | None = None match_quality: Annotated[ float | None, Field( description='Overall match quality score for the batch (0.0 = no matches, 1.0 = all matched)', ge=0.0, le=1.0, ), ] = None partial_failures: Annotated[ list[PartialFailure] | None, Field(description='Events that failed validation') ] = None warnings: Annotated[ list[str] | None, Field( description='Non-fatal issues (low match quality, missing recommended fields, deprecation notices)' ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar events_processed : intvar events_received : intvar ext : ExtensionObject | Nonevar match_quality : float | Nonevar model_configvar partial_failures : list[PartialFailure] | Nonevar warnings : list[str] | None
Inherited members
class LogEventErrorResponse (**data: Any)-
Expand source code
class LogEventResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[list[error.Error], Field(description='Operation-level errors', min_length=1)] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_config
Inherited members
class McpWebhookPayload (**data: Any)-
Expand source code
class McpWebhookPayload(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context_id: Annotated[ str | None, Field( description='Session/conversation identifier. Use this to continue the conversation if input-required status needs clarification or additional parameters.' ), ] = None domain: Annotated[ adcp_domain.AdcpDomain | None, Field( description='AdCP domain this task belongs to. Helps classify the operation type at a high level.' ), ] = None message: Annotated[ str | None, Field( description='Human-readable summary of the current task state. Provides context about what happened and what action may be needed.' ), ] = None operation_id: Annotated[ str | None, Field( description='Publisher-defined operation identifier correlating a sequence of task updates across webhooks.' ), ] = None result: Annotated[ async_response_data.AdcpAsyncResponseData | None, Field( description='Task-specific payload matching the status. For completed/failed, contains the full task response. For working/input-required/submitted, contains status-specific data. This is the data layer that AdCP specs - same structure used in A2A status.message.parts[].data.' ), ] = None status: Annotated[ task_status.TaskStatus, Field( description='Current task status. Webhooks are triggered for status changes after initial submission.' ), ] task_id: Annotated[ str, Field( description='Unique identifier for this task. Use this to correlate webhook notifications with the original task submission.' ), ] task_type: Annotated[ task_type_1.TaskType, Field( description='Type of AdCP operation that triggered this webhook. Enables webhook handlers to route to appropriate processing logic.' ), ] timestamp: Annotated[ AwareDatetime, Field(description='ISO 8601 timestamp when this webhook was generated.') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context_id : str | Nonevar domain : AdcpDomain | Nonevar message : str | Nonevar model_configvar operation_id : str | Nonevar result : AdcpAsyncResponseData | Nonevar status : TaskStatusvar task_id : strvar task_type : TaskTypevar timestamp : pydantic.types.AwareDatetime
Inherited members
class MediaBuy (**data: Any)-
Expand source code
class MediaBuy(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account: Annotated[ account_1.Account | None, Field(description='Account billed for this media buy') ] = None buyer_ref: Annotated[ str | None, Field(description="Buyer's reference identifier for this media buy") ] = None created_at: Annotated[AwareDatetime | None, Field(description='Creation timestamp')] = None creative_deadline: Annotated[ AwareDatetime | None, Field(description='ISO 8601 timestamp for creative upload deadline') ] = None ext: ext_1.ExtensionObject | None = None media_buy_id: Annotated[ str, Field(description="Publisher's unique identifier for the media buy") ] packages: Annotated[ list[package.Package], Field(description='Array of packages within this media buy') ] promoted_offering: Annotated[ str, Field(description='Description of advertiser and what is being promoted') ] status: media_buy_status.MediaBuyStatus total_budget: Annotated[float, Field(description='Total budget amount', ge=0.0)] updated_at: Annotated[AwareDatetime | None, Field(description='Last update timestamp')] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : Account | Nonevar buyer_ref : str | Nonevar created_at : pydantic.types.AwareDatetime | Nonevar creative_deadline : pydantic.types.AwareDatetime | Nonevar ext : ExtensionObject | Nonevar media_buy_id : strvar model_configvar packages : list[Package]var promoted_offering : strvar status : MediaBuyStatusvar total_budget : floatvar updated_at : pydantic.types.AwareDatetime | None
Inherited members
class MediaBuyStatus (*args, **kwds)-
Expand source code
class MediaBuyStatus(Enum): pending_activation = 'pending_activation' active = 'active' paused = 'paused' completed = 'completed'Create a collection of name/value pairs.
Example enumeration:
>>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3Access them by:
- attribute access::
>>> Color.RED <Color.RED: 1>- value lookup:
>>> Color(1) <Color.RED: 1>- name lookup:
>>> Color['RED'] <Color.RED: 1>Enumerations can be iterated over, and know how many members they have:
>>> len(Color) 3>>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.
Ancestors
- enum.Enum
Class variables
var activevar completedvar pausedvar pending_activation
class Package (**data: Any)-
Expand source code
class Package(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) bid_price: Annotated[ float | None, Field( description='Bid price for auction-based CPM pricing (present if using cpm-auction-option)', ge=0.0, ), ] = None budget: Annotated[ float | None, Field( description='Budget allocation for this package in the currency specified by the pricing option', ge=0.0, ), ] = None buyer_ref: Annotated[ str | None, Field(description="Buyer's reference identifier for this package") ] = None creative_assignments: Annotated[ list[creative_assignment.CreativeAssignment] | None, Field(description='Creative assets assigned to this package'), ] = None ext: ext_1.ExtensionObject | None = None format_ids_to_provide: Annotated[ list[format_id.FormatId] | None, Field(description='Format IDs that creative assets will be provided for this package'), ] = None impressions: Annotated[ float | None, Field(description='Impression goal for this package', ge=0.0) ] = None optimization_goal: optimization_goal_1.OptimizationGoal | None = None pacing: pacing_1.Pacing | None = None package_id: Annotated[str, Field(description="Publisher's unique identifier for the package")] paused: Annotated[ bool | None, Field( description='Whether this package is paused by the buyer. Paused packages do not deliver impressions. Defaults to false.' ), ] = False pricing_option_id: Annotated[ str | None, Field( description="ID of the selected pricing option from the product's pricing_options array" ), ] = None product_id: Annotated[ str | None, Field(description='ID of the product this package is based on') ] = None targeting_overlay: targeting.TargetingOverlay | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 bid_price : float | Nonevar budget : float | Nonevar buyer_ref : str | Nonevar creative_assignments : list[CreativeAssignment] | Nonevar ext : ExtensionObject | Nonevar format_ids_to_provide : list[FormatId] | Nonevar impressions : float | Nonevar model_configvar optimization_goal : OptimizationGoal | Nonevar pacing : Pacing | Nonevar package_id : strvar paused : bool | Nonevar pricing_option_id : str | Nonevar product_id : str | Nonevar targeting_overlay : TargetingOverlay | None
Inherited members
class PackageRequest (**data: Any)-
Expand source code
class PackageRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) bid_price: Annotated[ float | None, Field( description='Bid price for auction-based CPM pricing (required if using cpm-auction-option)', ge=0.0, ), ] = None budget: Annotated[ float, Field(description="Budget allocation for this package in the media buy's currency", ge=0.0), ] buyer_ref: Annotated[str, Field(description="Buyer's reference identifier for this package")] creative_assignments: Annotated[ list[creative_assignment.CreativeAssignment] | None, Field( description='Assign existing library creatives to this package with optional weights and placement targeting', min_length=1, ), ] = None creatives: Annotated[ list[creative_asset.CreativeAsset] | None, Field( description='Upload new creative assets and assign to this package (creatives will be added to library). Use creative_assignments instead for existing library creatives.', max_length=100, min_length=1, ), ] = None ext: ext_1.ExtensionObject | None = None format_ids: Annotated[ list[format_id.FormatId] | None, Field( description='Array of format IDs that will be used for this package - must be supported by the product. If omitted, defaults to all formats supported by the product.', min_length=1, ), ] = None impressions: Annotated[ float | None, Field(description='Impression goal for this package', ge=0.0) ] = None optimization_goal: optimization_goal_1.OptimizationGoal | None = None pacing: pacing_1.Pacing | None = None paused: Annotated[ bool | None, Field( description='Whether this package should be created in a paused state. Paused packages do not deliver impressions. Defaults to false.' ), ] = False pricing_option_id: Annotated[ str, Field( description="ID of the selected pricing option from the product's pricing_options array" ), ] product_id: Annotated[str, Field(description='Product ID for this package')] targeting_overlay: targeting.TargetingOverlay | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 bid_price : float | Nonevar budget : floatvar buyer_ref : strvar creative_assignments : list[CreativeAssignment] | Nonevar creatives : list[CreativeAsset] | Nonevar ext : ExtensionObject | Nonevar format_ids : list[FormatId] | Nonevar impressions : float | Nonevar model_configvar optimization_goal : OptimizationGoal | Nonevar pacing : Pacing | Nonevar paused : bool | Nonevar pricing_option_id : strvar product_id : strvar targeting_overlay : TargetingOverlay | None
Inherited members
class PreviewCreativeRequest (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class PreviewCreativeRequest( RootModel[PreviewCreativeRequest1 | PreviewCreativeRequest2 | PreviewCreativeRequest3] ): root: Annotated[ PreviewCreativeRequest1 | PreviewCreativeRequest2 | PreviewCreativeRequest3, Field( discriminator='request_type', description='Request to generate previews of one or more creative manifests. Accepts either a single creative request or an array of requests for batch processing.', title='Preview Creative Request', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[PreviewCreativeRequest1, PreviewCreativeRequest2, PreviewCreativeRequest3]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : PreviewCreativeRequest1 | PreviewCreativeRequest2 | PreviewCreativeRequest3
class PreviewCreativeFormatRequest (**data: Any)-
Expand source code
class PreviewCreativeRequest1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None creative_manifest: Annotated[ creative_manifest_1.CreativeManifest, Field( description='Complete creative manifest with all required assets (including promoted_offerings if required by the format)' ), ] ext: ext_1.ExtensionObject | None = None format_id: Annotated[ format_id_1.FormatId | None, Field( description='Format identifier for rendering the preview. Optional — defaults to creative_manifest_1.format_id if omitted.' ), ] = None inputs: Annotated[ list[Input] | None, Field( description='Array of input sets for generating multiple preview variants. Each input set defines macros and context values for one preview rendering. If not provided, creative agent will generate default previews.', min_length=1, ), ] = None output_format: Annotated[ preview_output_format.PreviewOutputFormat | None, Field( description="Output format for previews. 'url' returns preview_url (iframe-embeddable URL), 'html' returns preview_html (raw HTML for direct embedding). Default: 'url' for backward compatibility." ), ] = preview_output_format.PreviewOutputFormat.url request_type: Annotated[ Literal['single'], Field(description='Discriminator indicating this is a single preview request'), ] template_id: Annotated[ str | None, Field(description='Specific template ID for custom format rendering') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar creative_manifest : CreativeManifestvar ext : ExtensionObject | Nonevar format_id : FormatId | Nonevar inputs : list[Input] | Nonevar model_configvar output_format : PreviewOutputFormat | Nonevar request_type : Literal['single']var template_id : str | None
Inherited members
class PreviewCreativeManifestRequest (**data: Any)-
Expand source code
class PreviewCreativeRequest2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None output_format: Annotated[ preview_output_format.PreviewOutputFormat | None, Field( description="Default output format for all requests in this batch. Individual requests can override this. 'url' returns preview_url (iframe-embeddable URL), 'html' returns preview_html (raw HTML for direct embedding)." ), ] = preview_output_format.PreviewOutputFormat.url request_type: Annotated[ Literal['batch'], Field(description='Discriminator indicating this is a batch preview request'), ] requests: Annotated[ list[Request], Field( description='Array of preview requests (1-50 items). Each follows the single request structure.', max_length=50, min_length=1, ), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar ext : ExtensionObject | Nonevar model_configvar output_format : PreviewOutputFormat | Nonevar request_type : Literal['batch']var requests : list[Request]
Inherited members
class PreviewCreativeResponse (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class PreviewCreativeResponse( RootModel[PreviewCreativeResponse1 | PreviewCreativeResponse2 | PreviewCreativeResponse3] ): root: Annotated[ PreviewCreativeResponse1 | PreviewCreativeResponse2 | PreviewCreativeResponse3, Field( description='Response containing preview links for one or more creatives. Format matches the request: single preview response for single requests, batch results for batch requests.', title='Preview Creative Response', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[PreviewCreativeResponse1, PreviewCreativeResponse2, PreviewCreativeResponse3]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : PreviewCreativeResponse1 | PreviewCreativeResponse2 | PreviewCreativeResponse3
class PreviewCreativeStaticResponse (**data: Any)-
Expand source code
class PreviewCreativeResponse1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None expires_at: Annotated[ AwareDatetime, Field(description='ISO 8601 timestamp when preview links expire') ] ext: ext_1.ExtensionObject | None = None interactive_url: Annotated[ AnyUrl | None, Field( description='Optional URL to an interactive testing page that shows all preview variants with controls to switch between them, modify macro values, and test different scenarios.' ), ] = None previews: Annotated[ list[Preview], Field( description='Array of preview variants. Each preview corresponds to an input set from the request. If no inputs were provided, returns a single default preview.', min_length=1, ), ] response_type: Annotated[ Literal['single'], Field(description='Discriminator indicating this is a single preview response'), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar expires_at : pydantic.types.AwareDatetimevar ext : ExtensionObject | Nonevar interactive_url : pydantic.networks.AnyUrl | Nonevar model_configvar previews : list[Preview]var response_type : Literal['single']
Inherited members
class PreviewCreativeInteractiveResponse (**data: Any)-
Expand source code
class PreviewCreativeResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None response_type: Annotated[ Literal['batch'], Field(description='Discriminator indicating this is a batch preview response'), ] results: Annotated[ list[Results | Results1], Field( description='Array of preview results corresponding to each request in the same order. results[0] is the result for requests[0], results[1] for requests[1], etc. Order is guaranteed even when some requests fail. Each result contains either a successful preview response or an error.', min_length=1, ), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar ext : ExtensionObject | Nonevar model_configvar response_type : Literal['batch']var results : list[Results | Results1]
Inherited members
class UrlPreviewRender (**data: Any)-
Expand source code
class PreviewRender1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) dimensions: Annotated[ Dimensions | None, Field(description='Dimensions for this rendered piece') ] = None embedding: Annotated[ Embedding | None, Field(description='Optional security and embedding metadata for safe iframe integration'), ] = None output_format: Annotated[ Literal['url'], Field(description='Discriminator indicating preview_url is provided') ] preview_url: Annotated[ AnyUrl, Field( description='URL to an HTML page that renders this piece. Can be embedded in an iframe.' ), ] render_id: Annotated[ str, Field(description='Unique identifier for this rendered piece within the variant') ] role: Annotated[ str, Field( description="Semantic role of this rendered piece. Use 'primary' for main content, 'companion' for associated banners, descriptive strings for device variants or custom roles." ), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 dimensions : Dimensions | Nonevar embedding : Embedding | Nonevar model_configvar output_format : Literal['url']var preview_url : pydantic.networks.AnyUrlvar render_id : strvar role : str
Inherited members
class HtmlPreviewRender (**data: Any)-
Expand source code
class PreviewRender2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) dimensions: Annotated[ Dimensions | None, Field(description='Dimensions for this rendered piece') ] = None embedding: Annotated[ Embedding | None, Field(description='Optional security and embedding metadata') ] = None output_format: Annotated[ Literal['html'], Field(description='Discriminator indicating preview_html is provided') ] preview_html: Annotated[ str, Field( description='Raw HTML for this rendered piece. Can be embedded directly in the page without iframe. Security warning: Only use with trusted creative agents as this bypasses iframe sandboxing.' ), ] render_id: Annotated[ str, Field(description='Unique identifier for this rendered piece within the variant') ] role: Annotated[ str, Field( description="Semantic role of this rendered piece. Use 'primary' for main content, 'companion' for associated banners, descriptive strings for device variants or custom roles." ), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 dimensions : Dimensions | Nonevar embedding : Embedding | Nonevar model_configvar output_format : Literal['html']var preview_html : strvar render_id : strvar role : str
Inherited members
class BothPreviewRender (**data: Any)-
Expand source code
class PreviewRender3(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) dimensions: Annotated[ Dimensions | None, Field(description='Dimensions for this rendered piece') ] = None embedding: Annotated[ Embedding | None, Field(description='Optional security and embedding metadata for safe iframe integration'), ] = None output_format: Annotated[ Literal['both'], Field( description='Discriminator indicating both preview_url and preview_html are provided' ), ] preview_html: Annotated[ str, Field( description='Raw HTML for this rendered piece. Can be embedded directly in the page without iframe. Security warning: Only use with trusted creative agents as this bypasses iframe sandboxing.' ), ] preview_url: Annotated[ AnyUrl, Field( description='URL to an HTML page that renders this piece. Can be embedded in an iframe.' ), ] render_id: Annotated[ str, Field(description='Unique identifier for this rendered piece within the variant') ] role: Annotated[ str, Field( description="Semantic role of this rendered piece. Use 'primary' for main content, 'companion' for associated banners, descriptive strings for device variants or custom roles." ), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 dimensions : Dimensions | Nonevar embedding : Embedding | Nonevar model_configvar output_format : Literal['both']var preview_html : strvar preview_url : pydantic.networks.AnyUrlvar render_id : strvar role : str
Inherited members
class PriceGuidance (**data: Any)-
Expand source code
class PriceGuidance(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) p25: Annotated[ float | None, Field(description='25th percentile of recent winning bids', ge=0.0) ] = None p50: Annotated[float | None, Field(description='Median of recent winning bids', ge=0.0)] = None p75: Annotated[ float | None, Field(description='75th percentile of recent winning bids', ge=0.0) ] = None p90: Annotated[ float | None, Field(description='90th percentile of recent winning bids', ge=0.0) ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 model_configvar p25 : float | Nonevar p50 : float | Nonevar p75 : float | Nonevar p90 : float | None
Inherited members
class PricingModel (*args, **kwds)-
Expand source code
class PricingModel(Enum): cpm = 'cpm' vcpm = 'vcpm' cpc = 'cpc' cpcv = 'cpcv' cpv = 'cpv' cpp = 'cpp' cpa = 'cpa' flat_rate = 'flat_rate' time = 'time'Create a collection of name/value pairs.
Example enumeration:
>>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3Access them by:
- attribute access::
>>> Color.RED <Color.RED: 1>- value lookup:
>>> Color(1) <Color.RED: 1>- name lookup:
>>> Color['RED'] <Color.RED: 1>Enumerations can be iterated over, and know how many members they have:
>>> len(Color) 3>>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.
Ancestors
- enum.Enum
Class variables
var cpavar cpcvar cpcvvar cpmvar cppvar cpvvar flat_ratevar timevar vcpm
class Product (**data: Any)-
Expand source code
class Product(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) brief_relevance: Annotated[ str | None, Field( description='Explanation of why this product matches the brief (only included when brief is provided)' ), ] = None catalog_match: Annotated[ CatalogMatch | None, Field( description="When the buyer provides a brand_manifest with product_catalog, indicates which of the buyer's catalog items are eligible for this product. Enables buyers to make informed product_selector choices in create_media_buy. Only present for products where catalog matching is relevant (e.g. sponsored product listings on retail media). Sellers SHOULD include at least one of matched_gtins or matched_skus." ), ] = None channels: Annotated[ list[channels_1.MediaChannel] | None, Field( description="Advertising channels this product is sold as. Products inherit from their properties' supported_channels but may narrow the scope. For example, a product covering YouTube properties might be sold as ['ctv'] even though those properties support ['olv', 'social', 'ctv']." ), ] = None conversion_tracking: Annotated[ ConversionTracking | None, Field( description='Conversion tracking for this product. Presence indicates the product supports conversion-optimized delivery. Seller-level capabilities (supported event types, UID types, attribution windows) are declared in get_adcp_capabilities.' ), ] = None creative_policy: creative_policy_1.CreativePolicy | None = None data_provider_signals: Annotated[ list[data_provider_signal_selector.DataProviderSignalSelector] | None, Field( description="Data provider signals available for this product. Buyers fetch signal definitions from each data provider's adagents.json and can verify agent authorization." ), ] = None delivery_measurement: Annotated[ DeliveryMeasurement, Field( description='Measurement provider and methodology for delivery metrics. The buyer accepts the declared provider as the source of truth for the buy. REQUIRED for all products.' ), ] delivery_type: delivery_type_1.DeliveryType description: Annotated[ str, Field(description='Detailed description of the product and its inventory') ] estimated_exposures: Annotated[ int | None, Field(description='Estimated exposures/impressions for guaranteed products', ge=0), ] = None expires_at: Annotated[ AwareDatetime | None, Field(description='Expiration timestamp for custom products') ] = None ext: ext_1.ExtensionObject | None = None format_ids: Annotated[ list[format_id_1.FormatId], Field( description='Array of supported creative format IDs - structured format_id objects with agent_url and id' ), ] is_custom: Annotated[bool | None, Field(description='Whether this is a custom product')] = None measurement: measurement_1.Measurement | None = None name: Annotated[str, Field(description='Human-readable product name')] placements: Annotated[ list[placement.Placement] | None, Field( description='Optional array of specific placements within this product. When provided, buyers can target specific placements when assigning creatives.', min_length=1, ), ] = None pricing_options: Annotated[ list[pricing_option.PricingOption], Field(description='Available pricing models for this product', min_length=1), ] product_card: Annotated[ ProductCard | None, Field( description='Optional standard visual card (300x400px) for displaying this product in user interfaces. Can be rendered via preview_creative or pre-generated.' ), ] = None product_card_detailed: Annotated[ ProductCardDetailed | None, Field( description='Optional detailed card with carousel and full specifications. Provides rich product presentation similar to media kit pages.' ), ] = None product_id: Annotated[str, Field(description='Unique identifier for the product')] property_targeting_allowed: Annotated[ bool | None, Field( description="Whether buyers can filter this product to a subset of its publisher_properties. When false (default), the product is 'all or nothing' - buyers must accept all properties or the product is excluded from property_list filtering results." ), ] = False publisher_properties: Annotated[ list[publisher_property_selector.PublisherPropertySelector], Field( description="Publisher properties covered by this product. Buyers fetch actual property definitions from each publisher's adagents.json and validate agent authorization. Selection patterns mirror the authorization patterns in adagents.json for consistency.", min_length=1, ), ] reporting_capabilities: reporting_capabilities_1.ReportingCapabilities | None = None signal_targeting_allowed: Annotated[ bool | None, Field( description='Whether buyers can filter this product to a subset of its data_provider_signals. When false (default), the product includes all listed signals as a bundle. When true, buyers can target specific signals.' ), ] = FalseBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 brief_relevance : str | Nonevar catalog_match : CatalogMatch | Nonevar channels : list[MediaChannel] | Nonevar conversion_tracking : ConversionTracking | Nonevar creative_policy : CreativePolicy | Nonevar data_provider_signals : list[DataProviderSignalSelector] | Nonevar delivery_measurement : DeliveryMeasurementvar delivery_type : DeliveryTypevar description : strvar estimated_exposures : int | Nonevar expires_at : pydantic.types.AwareDatetime | Nonevar ext : ExtensionObject | Nonevar format_ids : list[FormatId]var is_custom : bool | Nonevar measurement : Measurement | Nonevar model_configvar name : strvar placements : list[Placement] | Nonevar pricing_options : list[PricingOption]var product_card : ProductCard | Nonevar product_card_detailed : ProductCardDetailed | Nonevar product_id : strvar property_targeting_allowed : bool | Nonevar publisher_properties : list[PublisherPropertySelector]var reporting_capabilities : ReportingCapabilities | Nonevar signal_targeting_allowed : bool | None
Inherited members
class ProductFilters (**data: Any)-
Expand source code
class ProductFilters(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) budget_range: Annotated[ BudgetRange | BudgetRange1 | None, Field(description='Budget range to filter appropriate products'), ] = None channels: Annotated[ list[channels_1.MediaChannel] | None, Field( description="Filter by advertising channels (e.g., ['display', 'ctv', 'dooh'])", min_length=1, ), ] = None countries: Annotated[ list[Country] | None, Field( description="Filter by country coverage using ISO 3166-1 alpha-2 codes (e.g., ['US', 'CA', 'GB']). Works for all inventory types.", min_length=1, ), ] = None delivery_type: delivery_type_1.DeliveryType | None = None end_date: Annotated[ date | None, Field( description='Campaign end date (ISO 8601 date format: YYYY-MM-DD) for availability checks' ), ] = None format_ids: Annotated[ list[format_id.FormatId] | None, Field(description='Filter by specific format IDs', min_length=1), ] = None format_types: Annotated[ list[format_category.FormatCategory] | None, Field(description='Filter by format types', min_length=1), ] = None is_fixed_price: Annotated[ bool | None, Field( description='Filter by pricing availability: true = products offering fixed pricing (at least one option with fixed_price), false = products offering auction pricing (at least one option without fixed_price). Products with both fixed and auction options match both true and false.' ), ] = None metros: Annotated[ list[Metro] | None, Field( description='Filter by metro coverage for locally-bound inventory (radio, DOOH, local TV). Use when products have DMA/metro-specific coverage. For digital inventory where products have broad coverage, use required_geo_targeting instead to filter by seller capability.', min_length=1, ), ] = None min_exposures: Annotated[ int | None, Field(description='Minimum exposures/impressions needed for measurement validity', ge=1), ] = None regions: Annotated[ list[Region] | None, Field( description="Filter by region coverage using ISO 3166-2 codes (e.g., ['US-NY', 'US-CA', 'GB-SCT']). Use for locally-bound inventory (regional OOH, local TV) where products have region-specific coverage.", min_length=1, ), ] = None required_axe_integrations: Annotated[ list[AnyUrl] | None, Field( description='Filter to products executable through specific agentic ad exchanges. URLs are canonical identifiers.' ), ] = None required_features: Annotated[ media_buy_features.MediaBuyFeatures | None, Field( description='Filter to products from sellers supporting specific protocol features. Only features set to true are used for filtering.' ), ] = None required_geo_targeting: Annotated[ list[RequiredGeoTargetingItem] | None, Field( description='Filter to products from sellers supporting specific geo targeting capabilities. Each entry specifies a targeting level (country, region, metro, postal_area) and optionally a system for levels that have multiple classification systems.', min_length=1, ), ] = None signal_targeting: Annotated[ list[signal_targeting_1.SignalTargeting] | None, Field( description='Filter to products supporting specific signals from data provider catalogs. Products must have the requested signals in their data_provider_signals and signal_targeting_allowed must be true (or all signals requested).', min_length=1, ), ] = None standard_formats_only: Annotated[ bool | None, Field(description='Only return products accepting IAB standard formats') ] = None start_date: Annotated[ date | None, Field( description='Campaign start date (ISO 8601 date format: YYYY-MM-DD) for availability checks' ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 budget_range : BudgetRange | BudgetRange1 | Nonevar channels : list[MediaChannel] | Nonevar countries : list[Country] | Nonevar delivery_type : DeliveryType | Nonevar end_date : datetime.date | Nonevar format_ids : list[FormatId] | Nonevar format_types : list[FormatCategory] | Nonevar is_fixed_price : bool | Nonevar metros : list[Metro] | Nonevar min_exposures : int | Nonevar model_configvar regions : list[Region] | Nonevar required_axe_integrations : list[pydantic.networks.AnyUrl] | Nonevar required_features : MediaBuyFeatures | Nonevar required_geo_targeting : list[RequiredGeoTargetingItem] | Nonevar signal_targeting : list[SignalTargeting] | Nonevar standard_formats_only : bool | Nonevar start_date : datetime.date | None
Inherited members
class Property (**data: Any)-
Expand source code
class Property(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) identifier: Annotated[ str, Field( description='Property identifier - domain for websites, bundle ID for apps', min_length=1, ), ] primary: Annotated[ bool | None, Field(description='Whether this is the primary property for the brand') ] = False region: Annotated[ str | None, Field( description="ISO 3166-1 alpha-2 country code or 'global'", pattern='^([A-Z]{2}|global)$' ), ] = None store: Annotated[Store | None, Field(description='App store for mobile/CTV apps')] = None type: Annotated[Type, Field(description='Property type')]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 identifier : strvar model_configvar primary : bool | Nonevar region : str | Nonevar store : Store | Nonevar type : Type
Inherited members
class PropertyId (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class PropertyId(RootModel[str]): root: Annotated[ str, Field( description='Identifier for a publisher property. Must be lowercase alphanumeric with underscores only.', examples=['cnn_ctv_app', 'homepage', 'mobile_ios', 'instagram'], pattern='^[a-z0-9_]+$', title='Property ID', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[str]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : str
class PropertyTag (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class PropertyTag(RootModel[str]): root: Annotated[ str, Field( description='Tag for categorizing publisher properties. Must be lowercase alphanumeric with underscores only.', examples=['ctv', 'premium', 'news', 'sports', 'meta_network', 'social_media'], pattern='^[a-z0-9_]+$', title='Property Tag', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[str]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : str
class Protocol (*args, **kwds)-
Expand source code
class Protocol(str, Enum): """Supported protocols.""" A2A = "a2a" MCP = "mcp"Supported protocols.
Ancestors
- builtins.str
- enum.Enum
Class variables
var A2Avar MCP
class ProvidePerformanceFeedbackRequest (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class ProvidePerformanceFeedbackRequest( RootModel[ProvidePerformanceFeedbackRequest1 | ProvidePerformanceFeedbackRequest2] ): root: Annotated[ ProvidePerformanceFeedbackRequest1 | ProvidePerformanceFeedbackRequest2, Field( description='Request payload for provide_performance_feedback task', title='Provide Performance Feedback Request', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[ProvidePerformanceFeedbackRequest1, ProvidePerformanceFeedbackRequest2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : ProvidePerformanceFeedbackRequest1 | ProvidePerformanceFeedbackRequest2
class ProvidePerformanceFeedbackResponse (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class ProvidePerformanceFeedbackResponse( RootModel[ProvidePerformanceFeedbackResponse1 | ProvidePerformanceFeedbackResponse2] ): root: Annotated[ ProvidePerformanceFeedbackResponse1 | ProvidePerformanceFeedbackResponse2, Field( description='Response payload for provide_performance_feedback task. Returns either success confirmation OR error information, never both.', title='Provide Performance Feedback Response', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[ProvidePerformanceFeedbackResponse1, ProvidePerformanceFeedbackResponse2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : ProvidePerformanceFeedbackResponse1 | ProvidePerformanceFeedbackResponse2
class ProvidePerformanceFeedbackSuccessResponse (**data: Any)-
Expand source code
class ProvidePerformanceFeedbackResponse1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None success: Annotated[ Literal[True], Field(description='Whether the performance feedback was successfully received'), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar ext : ExtensionObject | Nonevar model_configvar success : Literal[True]
Inherited members
class ProvidePerformanceFeedbackErrorResponse (**data: Any)-
Expand source code
class ProvidePerformanceFeedbackResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error], Field( description='Array of errors explaining why feedback was rejected (e.g., invalid measurement period, missing campaign data)', min_length=1, ), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_config
Inherited members
class PublisherPropertiesAll (**data: Any)-
Expand source code
class PublisherPropertySelector1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) publisher_domain: Annotated[ str, Field( description="Domain where publisher's adagents.json is hosted (e.g., 'cnn.com')", pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$', ), ] selection_type: Annotated[ Literal['all'], Field( description='Discriminator indicating all properties from this publisher are included' ), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 model_configvar publisher_domain : strvar selection_type : Literal['all']
Inherited members
class PublisherPropertiesById (**data: Any)-
Expand source code
class PublisherPropertySelector2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) property_ids: Annotated[ list[property_id.PropertyId], Field(description="Specific property IDs from the publisher's adagents.json", min_length=1), ] publisher_domain: Annotated[ str, Field( description="Domain where publisher's adagents.json is hosted (e.g., 'cnn.com')", pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$', ), ] selection_type: Annotated[ Literal['by_id'], Field(description='Discriminator indicating selection by specific property IDs'), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 model_configvar property_ids : list[PropertyId]var publisher_domain : strvar selection_type : Literal['by_id']
Inherited members
class PublisherPropertiesByTag (**data: Any)-
Expand source code
class PublisherPropertySelector3(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) property_tags: Annotated[ list[property_tag.PropertyTag], Field( description="Property tags from the publisher's adagents.json. Selector covers all properties with these tags", min_length=1, ), ] publisher_domain: Annotated[ str, Field( description="Domain where publisher's adagents.json is hosted (e.g., 'cnn.com')", pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$', ), ] selection_type: Annotated[ Literal['by_tag'], Field(description='Discriminator indicating selection by property tags') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 model_configvar publisher_domain : strvar selection_type : Literal['by_tag']
Inherited members
class PushNotificationConfig (**data: Any)-
Expand source code
class PushNotificationConfig(AdCPBaseModel): authentication: Annotated[ Authentication, Field(description='Authentication configuration for webhook delivery (A2A-compatible)'), ] token: Annotated[ str | None, Field( description='Optional client-provided token for webhook validation. Echoed back in webhook payload to validate request authenticity.', min_length=16, ), ] = None url: Annotated[AnyUrl, Field(description='Webhook endpoint URL for task status notifications')]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 authentication : Authenticationvar model_configvar token : str | Nonevar url : pydantic.networks.AnyUrl
Inherited members
class RegistryClient (base_url: str = 'https://agenticadvertising.org',
timeout: float = 10.0,
client: httpx.AsyncClient | None = None,
user_agent: str = 'adcp-client-python')-
Expand source code
class RegistryClient: """Client for the AdCP registry API. Provides brand and property lookups against the central AdCP registry. Args: base_url: Registry API base URL. timeout: Request timeout in seconds. client: Optional httpx.AsyncClient for connection pooling. If provided, caller is responsible for client lifecycle. user_agent: User-Agent header for requests. """ def __init__( self, base_url: str = DEFAULT_REGISTRY_URL, timeout: float = 10.0, client: httpx.AsyncClient | None = None, user_agent: str = "adcp-client-python", ): self._base_url = base_url.rstrip("/") self._timeout = timeout self._external_client = client self._owned_client: httpx.AsyncClient | None = None self._user_agent = user_agent async def _get_client(self) -> httpx.AsyncClient: """Get or create httpx client.""" if self._external_client is not None: return self._external_client if self._owned_client is None: self._owned_client = httpx.AsyncClient( limits=httpx.Limits( max_keepalive_connections=10, max_connections=20, ), ) return self._owned_client async def close(self) -> None: """Close owned HTTP client. No-op if using external client.""" if self._owned_client is not None: await self._owned_client.aclose() self._owned_client = None async def __aenter__(self) -> RegistryClient: return self async def __aexit__(self, *args: Any) -> None: await self.close() async def lookup_brand(self, domain: str) -> ResolvedBrand | None: """Resolve a single domain to its canonical brand identity. Args: domain: Domain to resolve (e.g., "nike.com"). Returns: ResolvedBrand if found, None if the domain is not in the registry. Raises: RegistryError: On HTTP or parsing errors. """ client = await self._get_client() try: response = await client.get( f"{self._base_url}/api/brands/resolve", params={"domain": domain}, headers={"User-Agent": self._user_agent}, timeout=self._timeout, ) if response.status_code == 404: return None if response.status_code != 200: raise RegistryError( f"Brand lookup failed: HTTP {response.status_code}", status_code=response.status_code, ) data = response.json() if data is None: return None return ResolvedBrand.model_validate(data) except RegistryError: raise except httpx.TimeoutException as e: raise RegistryError(f"Brand lookup timed out after {self._timeout}s") from e except httpx.HTTPError as e: raise RegistryError(f"Brand lookup failed: {e}") from e except (ValidationError, ValueError) as e: raise RegistryError(f"Brand lookup failed: invalid response: {e}") from e async def lookup_brands( self, domains: list[str] ) -> dict[str, ResolvedBrand | None]: """Bulk resolve domains to brand identities. Automatically chunks requests exceeding 100 domains. Args: domains: List of domains to resolve. Returns: Dict mapping each domain to its ResolvedBrand, or None if not found. Raises: RegistryError: On HTTP or parsing errors. """ if not domains: return {} chunks = [ domains[i : i + MAX_BULK_DOMAINS] for i in range(0, len(domains), MAX_BULK_DOMAINS) ] chunk_results = await asyncio.gather( *[self._lookup_brands_chunk(chunk) for chunk in chunks], return_exceptions=True, ) merged: dict[str, ResolvedBrand | None] = {} for result in chunk_results: if isinstance(result, BaseException): raise result merged.update(result) return merged async def _lookup_brands_chunk( self, domains: list[str] ) -> dict[str, ResolvedBrand | None]: """Resolve a single chunk of brand domains (max 100).""" client = await self._get_client() try: response = await client.post( f"{self._base_url}/api/brands/resolve/bulk", json={"domains": domains}, headers={"User-Agent": self._user_agent}, timeout=self._timeout, ) if response.status_code != 200: raise RegistryError( f"Bulk brand lookup failed: HTTP {response.status_code}", status_code=response.status_code, ) data = response.json() results_raw = data.get("results", {}) results: dict[str, ResolvedBrand | None] = {d: None for d in domains} for domain, brand_data in results_raw.items(): if brand_data is not None: results[domain] = ResolvedBrand.model_validate(brand_data) return results except RegistryError: raise except httpx.TimeoutException as e: raise RegistryError( f"Bulk brand lookup timed out after {self._timeout}s" ) from e except httpx.HTTPError as e: raise RegistryError(f"Bulk brand lookup failed: {e}") from e except (ValidationError, ValueError) as e: raise RegistryError(f"Bulk brand lookup failed: invalid response: {e}") from e async def lookup_property(self, domain: str) -> ResolvedProperty | None: """Resolve a publisher domain to its property info. Args: domain: Publisher domain to resolve (e.g., "nytimes.com"). Returns: ResolvedProperty if found, None if the domain is not in the registry. Raises: RegistryError: On HTTP or parsing errors. """ client = await self._get_client() try: response = await client.get( f"{self._base_url}/api/properties/resolve", params={"domain": domain}, headers={"User-Agent": self._user_agent}, timeout=self._timeout, ) if response.status_code == 404: return None if response.status_code != 200: raise RegistryError( f"Property lookup failed: HTTP {response.status_code}", status_code=response.status_code, ) data = response.json() if data is None: return None return ResolvedProperty.model_validate(data) except RegistryError: raise except httpx.TimeoutException as e: raise RegistryError( f"Property lookup timed out after {self._timeout}s" ) from e except httpx.HTTPError as e: raise RegistryError(f"Property lookup failed: {e}") from e except (ValidationError, ValueError) as e: raise RegistryError(f"Property lookup failed: invalid response: {e}") from e async def lookup_properties( self, domains: list[str] ) -> dict[str, ResolvedProperty | None]: """Bulk resolve publisher domains to property info. Automatically chunks requests exceeding 100 domains. Args: domains: List of publisher domains to resolve. Returns: Dict mapping each domain to its ResolvedProperty, or None if not found. Raises: RegistryError: On HTTP or parsing errors. """ if not domains: return {} chunks = [ domains[i : i + MAX_BULK_DOMAINS] for i in range(0, len(domains), MAX_BULK_DOMAINS) ] chunk_results = await asyncio.gather( *[self._lookup_properties_chunk(chunk) for chunk in chunks], return_exceptions=True, ) merged: dict[str, ResolvedProperty | None] = {} for result in chunk_results: if isinstance(result, BaseException): raise result merged.update(result) return merged async def _lookup_properties_chunk( self, domains: list[str] ) -> dict[str, ResolvedProperty | None]: """Resolve a single chunk of property domains (max 100).""" client = await self._get_client() try: response = await client.post( f"{self._base_url}/api/properties/resolve/bulk", json={"domains": domains}, headers={"User-Agent": self._user_agent}, timeout=self._timeout, ) if response.status_code != 200: raise RegistryError( f"Bulk property lookup failed: HTTP {response.status_code}", status_code=response.status_code, ) data = response.json() results_raw = data.get("results", {}) results: dict[str, ResolvedProperty | None] = {d: None for d in domains} for domain, prop_data in results_raw.items(): if prop_data is not None: results[domain] = ResolvedProperty.model_validate(prop_data) return results except RegistryError: raise except httpx.TimeoutException as e: raise RegistryError( f"Bulk property lookup timed out after {self._timeout}s" ) from e except httpx.HTTPError as e: raise RegistryError(f"Bulk property lookup failed: {e}") from e except (ValidationError, ValueError) as e: raise RegistryError( f"Bulk property lookup failed: invalid response: {e}" ) from eClient for the AdCP registry API.
Provides brand and property lookups against the central AdCP registry.
Args
base_url- Registry API base URL.
timeout- Request timeout in seconds.
client- Optional httpx.AsyncClient for connection pooling. If provided, caller is responsible for client lifecycle.
user_agent- User-Agent header for requests.
Methods
async def close(self) ‑> None-
Expand source code
async def close(self) -> None: """Close owned HTTP client. No-op if using external client.""" if self._owned_client is not None: await self._owned_client.aclose() self._owned_client = NoneClose owned HTTP client. No-op if using external client.
async def lookup_brand(self, domain: str) ‑> ResolvedBrand | None-
Expand source code
async def lookup_brand(self, domain: str) -> ResolvedBrand | None: """Resolve a single domain to its canonical brand identity. Args: domain: Domain to resolve (e.g., "nike.com"). Returns: ResolvedBrand if found, None if the domain is not in the registry. Raises: RegistryError: On HTTP or parsing errors. """ client = await self._get_client() try: response = await client.get( f"{self._base_url}/api/brands/resolve", params={"domain": domain}, headers={"User-Agent": self._user_agent}, timeout=self._timeout, ) if response.status_code == 404: return None if response.status_code != 200: raise RegistryError( f"Brand lookup failed: HTTP {response.status_code}", status_code=response.status_code, ) data = response.json() if data is None: return None return ResolvedBrand.model_validate(data) except RegistryError: raise except httpx.TimeoutException as e: raise RegistryError(f"Brand lookup timed out after {self._timeout}s") from e except httpx.HTTPError as e: raise RegistryError(f"Brand lookup failed: {e}") from e except (ValidationError, ValueError) as e: raise RegistryError(f"Brand lookup failed: invalid response: {e}") from eResolve a single domain to its canonical brand identity.
Args
domain- Domain to resolve (e.g., "nike.com").
Returns
ResolvedBrand if found, None if the domain is not in the registry.
Raises
RegistryError- On HTTP or parsing errors.
async def lookup_brands(self, domains: list[str]) ‑> dict[str, ResolvedBrand | None]-
Expand source code
async def lookup_brands( self, domains: list[str] ) -> dict[str, ResolvedBrand | None]: """Bulk resolve domains to brand identities. Automatically chunks requests exceeding 100 domains. Args: domains: List of domains to resolve. Returns: Dict mapping each domain to its ResolvedBrand, or None if not found. Raises: RegistryError: On HTTP or parsing errors. """ if not domains: return {} chunks = [ domains[i : i + MAX_BULK_DOMAINS] for i in range(0, len(domains), MAX_BULK_DOMAINS) ] chunk_results = await asyncio.gather( *[self._lookup_brands_chunk(chunk) for chunk in chunks], return_exceptions=True, ) merged: dict[str, ResolvedBrand | None] = {} for result in chunk_results: if isinstance(result, BaseException): raise result merged.update(result) return mergedBulk resolve domains to brand identities.
Automatically chunks requests exceeding 100 domains.
Args
domains- List of domains to resolve.
Returns
Dict mapping each domain to its ResolvedBrand, or None if not found.
Raises
RegistryError- On HTTP or parsing errors.
async def lookup_properties(self, domains: list[str]) ‑> dict[str, ResolvedProperty | None]-
Expand source code
async def lookup_properties( self, domains: list[str] ) -> dict[str, ResolvedProperty | None]: """Bulk resolve publisher domains to property info. Automatically chunks requests exceeding 100 domains. Args: domains: List of publisher domains to resolve. Returns: Dict mapping each domain to its ResolvedProperty, or None if not found. Raises: RegistryError: On HTTP or parsing errors. """ if not domains: return {} chunks = [ domains[i : i + MAX_BULK_DOMAINS] for i in range(0, len(domains), MAX_BULK_DOMAINS) ] chunk_results = await asyncio.gather( *[self._lookup_properties_chunk(chunk) for chunk in chunks], return_exceptions=True, ) merged: dict[str, ResolvedProperty | None] = {} for result in chunk_results: if isinstance(result, BaseException): raise result merged.update(result) return mergedBulk resolve publisher domains to property info.
Automatically chunks requests exceeding 100 domains.
Args
domains- List of publisher domains to resolve.
Returns
Dict mapping each domain to its ResolvedProperty, or None if not found.
Raises
RegistryError- On HTTP or parsing errors.
async def lookup_property(self, domain: str) ‑> ResolvedProperty | None-
Expand source code
async def lookup_property(self, domain: str) -> ResolvedProperty | None: """Resolve a publisher domain to its property info. Args: domain: Publisher domain to resolve (e.g., "nytimes.com"). Returns: ResolvedProperty if found, None if the domain is not in the registry. Raises: RegistryError: On HTTP or parsing errors. """ client = await self._get_client() try: response = await client.get( f"{self._base_url}/api/properties/resolve", params={"domain": domain}, headers={"User-Agent": self._user_agent}, timeout=self._timeout, ) if response.status_code == 404: return None if response.status_code != 200: raise RegistryError( f"Property lookup failed: HTTP {response.status_code}", status_code=response.status_code, ) data = response.json() if data is None: return None return ResolvedProperty.model_validate(data) except RegistryError: raise except httpx.TimeoutException as e: raise RegistryError( f"Property lookup timed out after {self._timeout}s" ) from e except httpx.HTTPError as e: raise RegistryError(f"Property lookup failed: {e}") from e except (ValidationError, ValueError) as e: raise RegistryError(f"Property lookup failed: invalid response: {e}") from eResolve a publisher domain to its property info.
Args
domain- Publisher domain to resolve (e.g., "nytimes.com").
Returns
ResolvedProperty if found, None if the domain is not in the registry.
Raises
RegistryError- On HTTP or parsing errors.
class RegistryError (message: str, status_code: int | None = None)-
Expand source code
class RegistryError(ADCPError): """Error from AdCP registry API operations (brand/property lookups).""" def __init__(self, message: str, status_code: int | None = None): """Initialize registry error.""" self.status_code = status_code suggestion = "Check that the registry API is accessible and the domain is valid." super().__init__(message, suggestion=suggestion)Error from AdCP registry API operations (brand/property lookups).
Initialize registry error.
Ancestors
- ADCPError
- builtins.Exception
- builtins.BaseException
class ResolvedBrand (**data: Any)-
Expand source code
class ResolvedBrand(BaseModel): """Brand identity resolved from the AdCP registry.""" model_config = ConfigDict(extra="allow") canonical_id: str canonical_domain: str brand_name: str names: list[dict[str, str]] | None = None keller_type: str | None = None parent_brand: str | None = None house_domain: str | None = None house_name: str | None = None brand_agent_url: str | None = None brand_manifest: dict[str, Any] | None = None source: strBrand identity resolved from the AdCP registry.
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
- pydantic.main.BaseModel
Class variables
var brand_agent_url : str | Nonevar brand_manifest : dict[str, typing.Any] | Nonevar brand_name : strvar canonical_domain : strvar canonical_id : strvar house_domain : str | Nonevar house_name : str | Nonevar keller_type : str | Nonevar model_configvar names : list[dict[str, str]] | Nonevar parent_brand : str | Nonevar source : str
class ResolvedProperty (**data: Any)-
Expand source code
class ResolvedProperty(BaseModel): """Property information resolved from the AdCP registry.""" model_config = ConfigDict(extra="allow") publisher_domain: str source: str authorized_agents: list[dict[str, Any]] properties: list[dict[str, Any]] verified: boolProperty information resolved from the AdCP registry.
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
- pydantic.main.BaseModel
Class variables
var model_configvar properties : list[dict[str, typing.Any]]var publisher_domain : strvar source : strvar verified : bool
class SignalFilters (**data: Any)-
Expand source code
class SignalFilters(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) catalog_types: Annotated[ list[signal_catalog_type.SignalCatalogType] | None, Field(description='Filter by catalog type', min_length=1), ] = None data_providers: Annotated[ list[str] | None, Field(description='Filter by specific data providers', min_length=1) ] = None max_cpm: Annotated[float | None, Field(description='Maximum CPM price filter', ge=0.0)] = None min_coverage_percentage: Annotated[ float | None, Field(description='Minimum coverage requirement', ge=0.0, le=100.0) ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 catalog_types : list[SignalCatalogType] | Nonevar data_providers : list[str] | Nonevar max_cpm : float | Nonevar min_coverage_percentage : float | Nonevar model_config
Inherited members
class MediaSubAsset (**data: Any)-
Expand source code
class SubAsset1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) asset_id: Annotated[ str, Field(description='Unique identifier for the asset within the creative') ] asset_kind: Annotated[ Literal['media'], Field(description='Discriminator indicating this is a media asset with content_uri'), ] asset_type: Annotated[ str, Field( description='Type of asset. Common types: thumbnail_image, product_image, featured_image, logo' ), ] content_uri: Annotated[AnyUrl, Field(description='URL for media assets (images, videos, etc.)')]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 asset_id : strvar asset_kind : Literal['media']var asset_type : strvar content_uri : pydantic.networks.AnyUrlvar model_config
Inherited members
class TextSubAsset (**data: Any)-
Expand source code
class SubAsset2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) asset_id: Annotated[ str, Field(description='Unique identifier for the asset within the creative') ] asset_kind: Annotated[ Literal['text'], Field(description='Discriminator indicating this is a text asset with content'), ] asset_type: Annotated[ str, Field( description='Type of asset. Common types: headline, body_text, cta_text, price_text, sponsor_name, author_name, click_url' ), ] content: Annotated[ str | list[str], Field( description='Text content for text-based assets like headlines, body text, CTA text, etc.' ), ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 asset_id : strvar asset_kind : Literal['text']var asset_type : strvar content : str | list[str]var model_config
Inherited members
class SyncAccountsRequest (**data: Any)-
Expand source code
class SyncAccountsRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) accounts: Annotated[ list[Account], Field(description='Advertiser accounts to sync', max_length=1000) ] context: context_1.ContextObject | None = None delete_missing: Annotated[ bool | None, Field( description='When true, accounts previously synced by this agent but not included in this request will be deactivated. Scoped to the authenticated agent — does not affect accounts managed by other agents. Use with caution.' ), ] = False dry_run: Annotated[ bool | None, Field( description='When true, preview what would change without applying. Returns what would be created/updated/deactivated.' ), ] = False ext: ext_1.ExtensionObject | None = None push_notification_config: Annotated[ push_notification_config_1.PushNotificationConfig | None, Field( description='Optional webhook for async notifications when account status changes (e.g., pending_approval transitions to active).' ), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 accounts : list[Account]var context : ContextObject | Nonevar delete_missing : bool | Nonevar dry_run : bool | Nonevar ext : ExtensionObject | Nonevar model_configvar push_notification_config : PushNotificationConfig | None
Inherited members
class SyncAccountsResponse (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class SyncAccountsResponse(RootModel[SyncAccountsResponse1 | SyncAccountsResponse2]): root: Annotated[ SyncAccountsResponse1 | SyncAccountsResponse2, Field( description='Response from account sync operation. Returns per-account results with status and billing, or operation-level errors on complete failure.', examples=[ { 'data': { 'accounts': [ { 'account_id': 'sub_tide_001', 'action': 'created', 'billing': 'agent', 'brand_id': 'tide', 'house': 'pg.com', 'name': 'Tide (via GroupM)', 'operator': 'groupm.com', 'parent_account_id': 'acc_agent_house', 'status': 'active', }, { 'account_id': 'acc_dove_pending', 'action': 'created', 'billing': 'brand', 'brand_id': 'dove', 'house': 'unilever.com', 'name': 'Dove', 'operator': 'mindshare.com', 'setup': { 'expires_at': '2026-03-10T00:00:00Z', 'message': 'Credit application required for direct billing', 'url': 'https://seller.com/onboard/dove', }, 'status': 'pending_approval', }, ] }, 'description': 'Mixed results - one active, one pending approval', }, { 'data': { 'accounts': [ { 'account_id': 'acc_agent_house', 'action': 'created', 'billing': 'agent', 'house': 'acme-corp.com', 'name': 'Acme Corp (via agent)', 'status': 'active', 'warnings': [ 'Direct billing (brand) not supported. Mapped to agent billing.' ], } ] }, 'description': "Seller doesn't support direct billing, maps to agent billing", }, ], title='Sync Accounts Response', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[SyncAccountsResponse1, SyncAccountsResponse2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : SyncAccountsResponse1 | SyncAccountsResponse2
class SyncAccountsSuccessResponse (**data: Any)-
Expand source code
class SyncAccountsResponse1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) accounts: Annotated[list[Account], Field(description='Results for each account processed')] context: context_1.ContextObject | None = None dry_run: Annotated[ bool | None, Field(description='Whether this was a dry run (no actual changes made)') ] = None ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 accounts : list[Account]var context : ContextObject | Nonevar dry_run : bool | Nonevar ext : ExtensionObject | Nonevar model_config
Inherited members
class SyncAccountsErrorResponse (**data: Any)-
Expand source code
class SyncAccountsResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error], Field( description='Operation-level errors (e.g., authentication failure, service unavailable)', min_length=1, ), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_config
Inherited members
class SyncCreativesRequest (**data: Any)-
Expand source code
class SyncCreativesRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account_id: Annotated[ str | None, Field( description='Account that owns these creatives. Optional if the agent has a single account or the seller can determine the account from context. Required if the agent has multiple accounts and the seller cannot route automatically.' ), ] = None assignments: Annotated[ dict[str, list[str]] | None, Field(description='Optional bulk assignment of creatives to packages'), ] = None context: context_1.ContextObject | None = None creative_ids: Annotated[ list[str] | None, Field( description='Optional filter to limit sync scope to specific creative IDs. When provided, only these creatives will be created/updated. Other creatives in the library are unaffected. Useful for partial updates and error recovery.', max_length=100, min_length=1, ), ] = None creatives: Annotated[ list[creative_asset.CreativeAsset], Field( description='Array of creative assets to sync (create or update)', max_length=100, min_length=1, ), ] delete_missing: Annotated[ bool | None, Field( description='When true, creatives not included in this sync will be archived. Use with caution for full library replacement.' ), ] = False dry_run: Annotated[ bool | None, Field( description='When true, preview changes without applying them. Returns what would be created/updated/deleted.' ), ] = False ext: ext_1.ExtensionObject | None = None push_notification_config: Annotated[ push_notification_config_1.PushNotificationConfig | None, Field( description='Optional webhook configuration for async sync notifications. Publisher will send webhook when sync completes if operation takes longer than immediate response time (typically for large bulk operations or manual approval/HITL).' ), ] = None validation_mode: Annotated[ validation_mode_1.ValidationMode | None, Field( description="Validation strictness. 'strict' fails entire sync on any validation error. 'lenient' processes valid creatives and reports errors." ), ] = validation_mode_1.ValidationMode.strictBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 : str | Nonevar assignments : dict[str, list[str]] | Nonevar context : ContextObject | Nonevar creative_ids : list[str] | Nonevar creatives : list[CreativeAsset]var delete_missing : bool | Nonevar dry_run : bool | Nonevar ext : ExtensionObject | Nonevar model_configvar push_notification_config : PushNotificationConfig | Nonevar validation_mode : ValidationMode | None
Inherited members
class SyncCreativesResponse (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class SyncCreativesResponse(RootModel[SyncCreativesResponse1 | SyncCreativesResponse2]): root: Annotated[ SyncCreativesResponse1 | SyncCreativesResponse2, Field( description='Response from creative sync operation. Returns either per-creative results (best-effort processing) OR operation-level errors (complete failure). This enforces atomic semantics at the operation level while allowing per-item failures within successful operations.', title='Sync Creatives Response', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[SyncCreativesResponse1, SyncCreativesResponse2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : SyncCreativesResponse1 | SyncCreativesResponse2
class SyncCreativesSuccessResponse (**data: Any)-
Expand source code
class SyncCreativesResponse1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None creatives: Annotated[ list[Creative], Field( description="Results for each creative processed. Items with action='failed' indicate per-item validation/processing failures, not operation-level failures." ), ] dry_run: Annotated[ bool | None, Field(description='Whether this was a dry run (no actual changes made)') ] = None ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar creatives : list[Creative]var dry_run : bool | Nonevar ext : ExtensionObject | Nonevar model_config
Inherited members
class SyncCreativesErrorResponse (**data: Any)-
Expand source code
class SyncCreativesResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error], Field( description='Operation-level errors that prevented processing any creatives (e.g., authentication failure, service unavailable, invalid request format)', min_length=1, ), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_config
Inherited members
class SyncEventSourcesRequest (**data: Any)-
Expand source code
class SyncEventSourcesRequest(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) account_id: Annotated[str, Field(description='Account to configure event sources for')] context: context_1.ContextObject | None = None delete_missing: Annotated[ bool | None, Field(description='When true, event sources not included in this sync will be removed'), ] = False event_sources: Annotated[ list[EventSource] | None, Field( description='Event sources to sync (create or update). When omitted, the call is discovery-only and returns all existing event sources on the account without modification.', min_length=1, ), ] = None ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar delete_missing : bool | Nonevar event_sources : list[EventSource] | Nonevar ext : ExtensionObject | Nonevar model_config
Inherited members
class SyncEventSourcesResponse (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class SyncEventSourcesResponse(RootModel[SyncEventSourcesResponse1 | SyncEventSourcesResponse2]): root: Annotated[ SyncEventSourcesResponse1 | SyncEventSourcesResponse2, Field( description='Response from event source sync operation. Returns either per-source results OR operation-level errors.', title='Sync Event Sources Response', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[SyncEventSourcesResponse1, SyncEventSourcesResponse2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : SyncEventSourcesResponse1 | SyncEventSourcesResponse2
class SyncEventSourcesSuccessResponse (**data: Any)-
Expand source code
class SyncEventSourcesResponse1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None event_sources: Annotated[ list[EventSource], Field( description='Results for each event source, including both synced and seller-managed sources on the account' ), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar event_sources : list[EventSource]var ext : ExtensionObject | Nonevar model_config
Inherited members
class SyncEventSourcesErrorResponse (**data: Any)-
Expand source code
class SyncEventSourcesResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error], Field(description='Operation-level errors that prevented processing', min_length=1), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_config
Inherited members
class TaskResult (**data: Any)-
Expand source code
class TaskResult(BaseModel, Generic[T]): """Result from task execution.""" model_config = ConfigDict(arbitrary_types_allowed=True) status: TaskStatus data: T | None = None message: str | None = None # Human-readable message from agent (e.g., MCP content text) submitted: SubmittedInfo | None = None needs_input: NeedsInputInfo | None = None error: str | None = None success: bool = Field(default=True) metadata: dict[str, Any] | None = None debug_info: DebugInfo | None = NoneResult from task execution.
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
- pydantic.main.BaseModel
- typing.Generic
Subclasses
- adcp.types.core.TaskResult[ActivateSignalResponse]
- adcp.types.core.TaskResult[AdcpAsyncResponseData]
- adcp.types.core.TaskResult[BuildCreativeResponse]
- adcp.types.core.TaskResult[CalibrateContentResponse]
- adcp.types.core.TaskResult[CreateContentStandardsResponse]
- adcp.types.core.TaskResult[CreateMediaBuyResponse]
- adcp.types.core.TaskResult[CreatePropertyListResponse]
- adcp.types.core.TaskResult[DeletePropertyListResponse]
- adcp.types.core.TaskResult[GetAdcpCapabilitiesResponse]
- adcp.types.core.TaskResult[GetContentStandardsResponse]
- adcp.types.core.TaskResult[GetCreativeDeliveryResponse]
- adcp.types.core.TaskResult[GetMediaBuyArtifactsResponse]
- adcp.types.core.TaskResult[GetMediaBuyDeliveryResponse]
- adcp.types.core.TaskResult[GetProductsResponse]
- adcp.types.core.TaskResult[GetPropertyListResponse]
- adcp.types.core.TaskResult[GetSignalsResponse]
- adcp.types.core.TaskResult[ListAccountsResponse]
- adcp.types.core.TaskResult[ListContentStandardsResponse]
- adcp.types.core.TaskResult[ListCreativeFormatsResponse]
- adcp.types.core.TaskResult[ListCreativesResponse]
- adcp.types.core.TaskResult[ListPropertyListsResponse]
- adcp.types.core.TaskResult[LogEventResponse]
- adcp.types.core.TaskResult[PreviewCreativeResponse]
- adcp.types.core.TaskResult[ProvidePerformanceFeedbackResponse]
- adcp.types.core.TaskResult[SiGetOfferingResponse]
- adcp.types.core.TaskResult[SiInitiateSessionResponse]
- adcp.types.core.TaskResult[SiSendMessageResponse]
- adcp.types.core.TaskResult[SiTerminateSessionResponse]
- adcp.types.core.TaskResult[SyncAccountsResponse]
- adcp.types.core.TaskResult[SyncCreativesResponse]
- adcp.types.core.TaskResult[SyncEventSourcesResponse]
- adcp.types.core.TaskResult[UpdateContentStandardsResponse]
- adcp.types.core.TaskResult[UpdateMediaBuyResponse]
- adcp.types.core.TaskResult[UpdatePropertyListResponse]
- adcp.types.core.TaskResult[ValidateContentDeliveryResponse]
Class variables
var data : ~T | Nonevar debug_info : DebugInfo | Nonevar error : str | Nonevar message : str | Nonevar metadata : dict[str, typing.Any] | Nonevar model_configvar needs_input : NeedsInputInfo | Nonevar status : TaskStatusvar submitted : SubmittedInfo | Nonevar success : bool
class TaskStatus (*args, **kwds)-
Expand source code
class TaskStatus(str, Enum): """Task execution status.""" COMPLETED = "completed" SUBMITTED = "submitted" NEEDS_INPUT = "needs_input" FAILED = "failed" WORKING = "working"Task execution status.
Ancestors
- builtins.str
- enum.Enum
Class variables
var COMPLETEDvar FAILEDvar NEEDS_INPUTvar SUBMITTEDvar WORKING
class GeneratedTaskStatus (*args, **kwds)-
Expand source code
class TaskStatus(Enum): submitted = 'submitted' working = 'working' input_required = 'input-required' completed = 'completed' canceled = 'canceled' failed = 'failed' rejected = 'rejected' auth_required = 'auth-required' unknown = 'unknown'Create a collection of name/value pairs.
Example enumeration:
>>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3Access them by:
- attribute access::
>>> Color.RED <Color.RED: 1>- value lookup:
>>> Color(1) <Color.RED: 1>- name lookup:
>>> Color['RED'] <Color.RED: 1>Enumerations can be iterated over, and know how many members they have:
>>> len(Color) 3>>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.
Ancestors
- enum.Enum
Class variables
var auth_requiredvar canceledvar completedvar failedvar input_requiredvar rejectedvar submittedvar unknownvar working
class UpdateMediaBuyRequest (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class UpdateMediaBuyRequest(RootModel[UpdateMediaBuyRequest1 | UpdateMediaBuyRequest2]): root: Annotated[ UpdateMediaBuyRequest1 | UpdateMediaBuyRequest2, Field( description='Request parameters for updating campaign and package settings', title='Update Media Buy Request', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[UpdateMediaBuyRequest1, UpdateMediaBuyRequest2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : UpdateMediaBuyRequest1 | UpdateMediaBuyRequest2
class UpdateMediaBuyPackagesRequest (**data: Any)-
Expand source code
class UpdateMediaBuyRequest1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) buyer_ref: Annotated[ str | None, Field(description="Buyer's reference for the media buy to update") ] = None context: context_1.ContextObject | None = None end_time: Annotated[ AwareDatetime | None, Field(description='New end date/time in ISO 8601 format') ] = None ext: ext_1.ExtensionObject | None = None media_buy_id: Annotated[str, Field(description="Publisher's ID of the media buy to update")] packages: Annotated[ list[package_update.PackageUpdate] | None, Field(description='Package-specific updates', min_length=1), ] = None paused: Annotated[ bool | None, Field(description='Pause/resume the entire media buy (true = paused, false = active)'), ] = None push_notification_config: Annotated[ push_notification_config_1.PushNotificationConfig | None, Field( description='Optional webhook configuration for async update notifications. Publisher will send webhook when update completes if operation takes longer than immediate response time. This is separate from reporting_webhook which configures ongoing campaign reporting.' ), ] = None reporting_webhook: Annotated[ reporting_webhook_1.ReportingWebhook | None, Field( description='Optional webhook configuration for automated reporting delivery. Updates the reporting configuration for this media buy.' ), ] = None start_time: start_timing.StartTiming | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 buyer_ref : str | Nonevar context : ContextObject | Nonevar end_time : pydantic.types.AwareDatetime | Nonevar ext : ExtensionObject | Nonevar media_buy_id : strvar model_configvar packages : list[PackageUpdate] | Nonevar paused : bool | Nonevar push_notification_config : PushNotificationConfig | Nonevar reporting_webhook : ReportingWebhook | Nonevar start_time : StartTiming | None
Inherited members
class UpdateMediaBuyPropertiesRequest (**data: Any)-
Expand source code
class UpdateMediaBuyRequest2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) buyer_ref: Annotated[str, Field(description="Buyer's reference for the media buy to update")] context: context_1.ContextObject | None = None end_time: Annotated[ AwareDatetime | None, Field(description='New end date/time in ISO 8601 format') ] = None ext: ext_1.ExtensionObject | None = None media_buy_id: Annotated[ str | None, Field(description="Publisher's ID of the media buy to update") ] = None packages: Annotated[ list[package_update.PackageUpdate] | None, Field(description='Package-specific updates', min_length=1), ] = None paused: Annotated[ bool | None, Field(description='Pause/resume the entire media buy (true = paused, false = active)'), ] = None push_notification_config: Annotated[ push_notification_config_1.PushNotificationConfig | None, Field( description='Optional webhook configuration for async update notifications. Publisher will send webhook when update completes if operation takes longer than immediate response time. This is separate from reporting_webhook which configures ongoing campaign reporting.' ), ] = None reporting_webhook: Annotated[ reporting_webhook_1.ReportingWebhook | None, Field( description='Optional webhook configuration for automated reporting delivery. Updates the reporting configuration for this media buy.' ), ] = None start_time: start_timing.StartTiming | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 buyer_ref : strvar context : ContextObject | Nonevar end_time : pydantic.types.AwareDatetime | Nonevar ext : ExtensionObject | Nonevar media_buy_id : str | Nonevar model_configvar packages : list[PackageUpdate] | Nonevar paused : bool | Nonevar push_notification_config : PushNotificationConfig | Nonevar reporting_webhook : ReportingWebhook | Nonevar start_time : StartTiming | None
Inherited members
class UpdateMediaBuyResponse (root: RootModelRootType = PydanticUndefined, **data)-
Expand source code
class UpdateMediaBuyResponse(RootModel[UpdateMediaBuyResponse1 | UpdateMediaBuyResponse2]): root: Annotated[ UpdateMediaBuyResponse1 | UpdateMediaBuyResponse2, Field( description='Response payload for update_media_buy task. Returns either complete success data OR error information, never both. This enforces atomic operation semantics - updates are either fully applied or not applied at all.', title='Update Media Buy Response', ), ]Usage Documentation
A Pydantic
BaseModelfor 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.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.root_model.RootModel[Union[UpdateMediaBuyResponse1, UpdateMediaBuyResponse2]]
- pydantic.root_model.RootModel
- pydantic.main.BaseModel
- typing.Generic
Class variables
var model_configvar root : UpdateMediaBuyResponse1 | UpdateMediaBuyResponse2
class UpdateMediaBuySuccessResponse (**data: Any)-
Expand source code
class UpdateMediaBuyResponse1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) affected_packages: Annotated[ list[package.Package] | None, Field(description='Array of packages that were modified with complete state information'), ] = None buyer_ref: Annotated[str, Field(description="Buyer's reference identifier for the media buy")] context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None implementation_date: Annotated[ AwareDatetime | None, Field(description='ISO 8601 timestamp when changes take effect (null if pending approval)'), ] = None media_buy_id: Annotated[str, Field(description="Publisher's identifier for the media buy")]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 affected_packages : list[Package] | Nonevar buyer_ref : strvar context : ContextObject | Nonevar ext : ExtensionObject | Nonevar implementation_date : pydantic.types.AwareDatetime | Nonevar media_buy_id : strvar model_config
Inherited members
class UpdateMediaBuyErrorResponse (**data: Any)-
Expand source code
class UpdateMediaBuyResponse2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) context: context_1.ContextObject | None = None errors: Annotated[ list[error.Error], Field(description='Array of errors explaining why the operation failed', min_length=1), ] ext: ext_1.ExtensionObject | None = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_config
Inherited members
class ValidateContentDeliverySuccessResponse (**data: Any)-
Expand source code
class ValidateContentDeliveryResponse1(AdCPBaseModel): context: context_1.ContextObject | None = None errors: Annotated[ Any | None, Field(description='Field must not be present in success response') ] = None ext: ext_1.ExtensionObject | None = None results: Annotated[list[Result], Field(description='Per-record evaluation results')] summary: Annotated[Summary, Field(description='Summary counts across all records')]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : typing.Any | Nonevar ext : ExtensionObject | Nonevar model_configvar results : list[Result]var summary : Summary
Inherited members
class ValidateContentDeliveryErrorResponse (**data: Any)-
Expand source code
class ValidateContentDeliveryResponse2(AdCPBaseModel): context: context_1.ContextObject | None = None errors: list[error.Error] ext: ext_1.ExtensionObject | None = None summary: Annotated[ Any | None, Field(description='Field must not be present in error response') ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 context : ContextObject | Nonevar errors : list[Error]var ext : ExtensionObject | Nonevar model_configvar summary : typing.Any | None
Inherited members
class ValidationError (*args, **kwargs)-
Expand source code
class ValidationError(ValueError): """Raised when runtime validation fails.""" passRaised when runtime validation fails.
Ancestors
- builtins.ValueError
- builtins.Exception
- builtins.BaseException
class UrlVastAsset (**data: Any)-
Expand source code
class VastAsset1(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) audio_description_url: Annotated[ AnyUrl | None, Field(description='URL to audio description track for visually impaired users'), ] = None captions_url: Annotated[ AnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)') ] = None delivery_type: Annotated[ Literal['url'], Field(description='Discriminator indicating VAST is delivered via URL endpoint'), ] duration_ms: Annotated[ int | None, Field(description='Expected video duration in milliseconds (if known)', ge=0) ] = None tracking_events: Annotated[ list[vast_tracking_event.VastTrackingEvent] | None, Field(description='Tracking events supported by this VAST tag'), ] = None url: Annotated[AnyUrl, Field(description='URL endpoint that returns VAST XML')] vast_version: Annotated[ vast_version_1.VastVersion | None, Field(description='VAST specification version') ] = None vpaid_enabled: Annotated[ bool | None, Field(description='Whether VPAID (Video Player-Ad Interface Definition) is supported'), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 audio_description_url : pydantic.networks.AnyUrl | Nonevar captions_url : pydantic.networks.AnyUrl | Nonevar delivery_type : Literal['url']var duration_ms : int | Nonevar model_configvar tracking_events : list[VastTrackingEvent] | Nonevar url : pydantic.networks.AnyUrlvar vast_version : VastVersion | Nonevar vpaid_enabled : bool | None
Inherited members
class InlineVastAsset (**data: Any)-
Expand source code
class VastAsset2(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) audio_description_url: Annotated[ AnyUrl | None, Field(description='URL to audio description track for visually impaired users'), ] = None captions_url: Annotated[ AnyUrl | None, Field(description='URL to captions file (WebVTT, SRT, etc.)') ] = None content: Annotated[str, Field(description='Inline VAST XML content')] delivery_type: Annotated[ Literal['inline'], Field(description='Discriminator indicating VAST is delivered as inline XML content'), ] duration_ms: Annotated[ int | None, Field(description='Expected video duration in milliseconds (if known)', ge=0) ] = None tracking_events: Annotated[ list[vast_tracking_event.VastTrackingEvent] | None, Field(description='Tracking events supported by this VAST tag'), ] = None vast_version: Annotated[ vast_version_1.VastVersion | None, Field(description='VAST specification version') ] = None vpaid_enabled: Annotated[ bool | None, Field(description='Whether VPAID (Video Player-Ad Interface Definition) is supported'), ] = NoneBase model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 audio_description_url : pydantic.networks.AnyUrl | Nonevar captions_url : pydantic.networks.AnyUrl | Nonevar content : strvar delivery_type : Literal['inline']var duration_ms : int | Nonevar model_configvar tracking_events : list[VastTrackingEvent] | Nonevar vast_version : VastVersion | Nonevar vpaid_enabled : bool | None
Inherited members
class VcpmPricingOption (**data: Any)-
Expand source code
class VcpmPricingOption(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) currency: Annotated[ str, Field( description='ISO 4217 currency code', examples=['USD', 'EUR', 'GBP', 'JPY'], pattern='^[A-Z]{3}$', ), ] fixed_price: Annotated[ float | None, Field( description='Fixed price per unit. If present, this is fixed pricing. If absent, auction-based.', ge=0.0, ), ] = None floor_price: Annotated[ float | None, Field( description='Minimum acceptable bid for auction pricing (mutually exclusive with fixed_price). Bids below this value will be rejected.', ge=0.0, ), ] = None min_spend_per_package: Annotated[ float | None, Field( description='Minimum spend requirement per package using this pricing option, in the specified currency', ge=0.0, ), ] = None price_guidance: Annotated[ price_guidance_1.PriceGuidance | None, Field(description='Optional pricing guidance for auction-based bidding'), ] = None pricing_model: Annotated[ Literal['vcpm'], Field(description='Cost per 1,000 viewable impressions (MRC standard)') ] pricing_option_id: Annotated[ str, Field(description='Unique identifier for this pricing option within the product') ]Base model for AdCP types with spec-compliant serialization.
AdCP JSON schemas use additionalProperties: false and do not allow null for optional fields. Therefore, optional fields must be omitted entirely when not present (not sent as null).
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 currency : strvar fixed_price : float | Nonevar floor_price : float | Nonevar min_spend_per_package : float | Nonevar model_configvar price_guidance : PriceGuidance | Nonevar pricing_model : Literal['vcpm']var pricing_option_id : str
Inherited members
class WebhookMetadata (**data: Any)-
Expand source code
class WebhookMetadata(BaseModel): """Metadata passed to webhook handlers.""" operation_id: str agent_id: str task_type: str status: TaskStatus sequence_number: int | None = None notification_type: Literal["scheduled", "final", "delayed"] | None = None timestamp: strMetadata passed to webhook handlers.
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
- pydantic.main.BaseModel
Class variables
var agent_id : strvar model_configvar notification_type : Literal['scheduled', 'final', 'delayed'] | Nonevar operation_id : strvar sequence_number : int | Nonevar status : TaskStatusvar task_type : strvar timestamp : str