Module adcp.server.responses
Response builder helpers for ADCP servers.
These functions produce correctly-shaped AdCP response dicts that match the generated Pydantic response schemas. They reduce boilerplate and ensure schema compliance.
Every builder here matches the field names in the corresponding generated response type (e.g., SyncAccountsResponse uses "accounts", SyncCreativesResponse uses "creatives").
Usage
from adcp.server.responses import capabilities_response, products_response
@mcp.tool() async def get_adcp_capabilities(): return capabilities_response(["media_buy"])
@mcp.tool() async def get_products(): return products_response(MY_PRODUCTS)
Functions
def activate_signal_response(deployments: list[dict[str, Any]], *, sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def activate_signal_response( deployments: list[dict[str, Any]], *, sandbox: bool = True, ) -> dict[str, Any]: """Build an activate_signal success response. Each deployment should include: type, is_live, activation_key. For platform: platform, account. For agent: agent_url. Matches ActivateSignalResponse1 (success) schema. """ return { "deployments": deployments, "sandbox": sandbox, }Build an activate_signal success response.
Each deployment should include: type, is_live, activation_key. For platform: platform, account. For agent: agent_url. Matches ActivateSignalResponse1 (success) schema.
def build_creative_response(creative_manifest: dict[str, Any] | list[dict[str, Any]],
*,
sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def build_creative_response( creative_manifest: dict[str, Any] | list[dict[str, Any]], *, sandbox: bool = True, ) -> dict[str, Any]: """Build a build_creative success response. Accepts either a single manifest dict or a list of manifests. Each manifest should include: format_id, name, assets. Single manifest matches BuildCreativeResponse1. List matches BuildCreativeResponse3 (multi-format). """ if isinstance(creative_manifest, list): return { "creative_manifests": [_strip_none_values(m) for m in creative_manifest], "sandbox": sandbox, } return { "creative_manifest": _strip_none_values(creative_manifest), "sandbox": sandbox, }Build a build_creative success response.
Accepts either a single manifest dict or a list of manifests. Each manifest should include: format_id, name, assets.
Single manifest matches BuildCreativeResponse1. List matches BuildCreativeResponse3 (multi-format).
def capabilities_response(supported_protocols: list[str],
*,
major_versions: list[int] | None = None,
adcp_version: str | None = None,
supported_versions: list[str] | None = None,
build_version: str | None = None,
sandbox: bool = True,
features: dict[str, Any] | None = None,
idempotency: dict[str, Any] | None = None,
compliance_testing: dict[str, Any] | None = None) ‑> dict[str, typing.Any]-
Expand source code
def capabilities_response( supported_protocols: list[str], *, major_versions: list[int] | None = None, adcp_version: str | None = None, supported_versions: list[str] | None = None, build_version: str | None = None, sandbox: bool = True, features: dict[str, Any] | None = None, idempotency: dict[str, Any] | None = None, compliance_testing: dict[str, Any] | None = None, ) -> dict[str, Any]: """Build a get_adcp_capabilities response. Args: supported_protocols: e.g. ["media_buy"], ["media_buy", "signals"]. Valid values: media_buy, signals, governance, creative, brand, sponsored_intelligence. ``compliance_testing`` is NOT a protocol — pass it via the ``compliance_testing`` kwarg. major_versions: AdCP major versions. Defaults to [3]. Deprecated in favor of ``supported_versions`` (release-precision); both are emitted through 3.x for backwards compatibility. adcp_version: Server's pinned release this response was built for. Must match one of ``supported_versions`` exactly. When set, included on the response envelope so buyers can read what release the server actually served. Typically passed by ``ADCPServerBuilder``'s auto-capabilities handler from its per-instance pin. supported_versions: Release-precision versions this server speaks (e.g. ``["3.0", "3.1"]``). Authoritative for buyer-side release pinning per the version-negotiation RFC. When omitted, defaults to the SDK's stable aliases plus the exact packaged prerelease line when applicable. build_version: Optional advisory metadata — full VERSION.RELEASE.PATCH of the server's build (e.g. ``"3.1.2"``). Useful for incident triage; not part of the wire negotiation contract. sandbox: Whether this is a sandbox agent. Defaults to True. features: Additional feature flags. idempotency: Optional idempotency declaration, nested under ``adcp.idempotency`` per AdCP #2315. Pass the output of :meth:`adcp.server.idempotency.IdempotencyStore.capability` here to declare the seller's ``replay_ttl_seconds``. compliance_testing: Optional top-level ``compliance_testing`` block to advertise compliance-testing capabilities. When provided, emitted as a sibling of ``adcp`` in the response. Example:: from adcp.server.responses import capabilities_response from adcp.server.idempotency import IdempotencyStore, MemoryBackend store = IdempotencyStore(backend=MemoryBackend(), ttl_seconds=86400) return capabilities_response( ["media_buy"], idempotency=store.capability(), ) """ if compliance_testing is not None and not idempotency: _logger.warning( "capabilities_response: adcp.idempotency not declared. " "The AdCP 3.0.1 storyboard runner may downgrade to v2 mode and " "cascade failures across idempotency-dependent tracks. " "Pass idempotency={'supported': False} to declare non-support, " "or idempotency=store.capability() to declare support." ) effective_major_versions = major_versions or [ADCP_MAJOR_VERSION] adcp_info: dict[str, Any] = {"major_versions": effective_major_versions} if supported_versions is None: majors = _major_version_values(effective_major_versions) if majors and all(major == ADCP_MAJOR_VERSION for major in majors): supported_versions = list(get_supported_adcp_versions()) if supported_versions: adcp_info["supported_versions"] = supported_versions if build_version is not None: adcp_info["build_version"] = build_version if idempotency: adcp_info["idempotency"] = idempotency resp: dict[str, Any] = { "status": "completed", "adcp": adcp_info, "supported_protocols": supported_protocols, "sandbox": sandbox, } if adcp_version is not None: resp["adcp_version"] = _normalize_capabilities_adcp_version( adcp_version, supported_versions, ) if features: resp["features"] = features if compliance_testing is not None: resp["compliance_testing"] = compliance_testing return respBuild a get_adcp_capabilities response.
Args
supported_protocols- e.g. ["media_buy"], ["media_buy", "signals"].
Valid values: media_buy, signals, governance, creative, brand,
sponsored_intelligence.
compliance_testingis NOT a protocol — pass it via thecompliance_testingkwarg. major_versions- AdCP major versions. Defaults to [3]. Deprecated in
favor of
supported_versions(release-precision); both are emitted through 3.x for backwards compatibility. adcp_version- Server's pinned release this response was built
for. Must match one of
supported_versionsexactly. When set, included on the response envelope so buyers can read what release the server actually served. Typically passed byADCPServerBuilder's auto-capabilities handler from its per-instance pin. supported_versions- Release-precision versions this server speaks
(e.g.
["3.0", "3.1"]). Authoritative for buyer-side release pinning per the version-negotiation RFC. When omitted, defaults to the SDK's stable aliases plus the exact packaged prerelease line when applicable. build_version- Optional advisory metadata — full
VERSION.RELEASE.PATCH of the server's build (e.g.
"3.1.2"). Useful for incident triage; not part of the wire negotiation contract. sandbox- Whether this is a sandbox agent. Defaults to True.
features- Additional feature flags.
idempotency- Optional idempotency declaration, nested under
adcp.idempotencyper AdCP #2315. Pass the output of :meth:IdempotencyStore.capability()here to declare the seller'sreplay_ttl_seconds. compliance_testing- Optional top-level
compliance_testingblock to advertise compliance-testing capabilities. When provided, emitted as a sibling ofadcpin the response.
Example::
from adcp.server.responses import capabilities_response from adcp.server.idempotency import IdempotencyStore, MemoryBackend store = IdempotencyStore(backend=MemoryBackend(), ttl_seconds=86400) return capabilities_response( ["media_buy"], idempotency=store.capability(), ) def creative_formats_response(formats: list[Any], *, sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def creative_formats_response( formats: list[Any], *, sandbox: bool = True, ) -> dict[str, Any]: """Build a list_creative_formats response. Each format should include: format_id ({agent_url, id}), name. Matches ListCreativeFormatsResponse schema. """ return { "formats": _serialize(formats), "sandbox": sandbox, }Build a list_creative_formats response.
Each format should include: format_id ({agent_url, id}), name. Matches ListCreativeFormatsResponse schema.
def delivery_response(media_buy_deliveries: list[dict[str, Any]],
*,
reporting_period: dict[str, str] | None = None,
currency: str = 'USD',
sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def delivery_response( media_buy_deliveries: list[dict[str, Any]], *, reporting_period: dict[str, str] | None = None, currency: str = "USD", sandbox: bool = True, ) -> dict[str, Any]: """Build a get_media_buy_delivery response. Each media_buy_delivery should include: media_buy_id, status, totals (impressions, spend, etc.), by_package. Matches GetMediaBuyDeliveryResponse schema. Args: media_buy_deliveries: Array of delivery data per media buy. reporting_period: {"start": ISO timestamp, "end": ISO timestamp}. Defaults to current timestamp for both. currency: ISO 4217 currency code. sandbox: Whether this is simulated data. """ now = _rfc3339_now() return { "reporting_period": reporting_period or {"start": now, "end": now}, "media_buy_deliveries": media_buy_deliveries, "currency": currency, "sandbox": sandbox, }Build a get_media_buy_delivery response.
Each media_buy_delivery should include: media_buy_id, status, totals (impressions, spend, etc.), by_package.
Matches GetMediaBuyDeliveryResponse schema.
Args
media_buy_deliveries- Array of delivery data per media buy.
reporting_period- {"start": ISO timestamp, "end": ISO timestamp}. Defaults to current timestamp for both.
currency- ISO 4217 currency code.
sandbox- Whether this is simulated data.
def error_response(code: str, message: str) ‑> dict[str, typing.Any]-
Expand source code
def error_response(code: str, message: str) -> dict[str, Any]: """Build a single AdCP error object (not a full error response). .. deprecated:: Use ``adcp_error()`` from ``adcp.server.helpers`` instead. It returns a properly wrapped ``{"errors": [...]}`` response with auto-recovery classification. This function returns an unwrapped single error dict ``{"code": ..., "message": ...}`` which is not a valid ADCP error response on its own. """ return {"code": code, "message": message}Build a single AdCP error object (not a full error response).
Deprecated
Use
adcp_error()fromadcp.server.helpersinstead. It returns a properly wrapped{"errors": [...]}response with auto-recovery classification. This function returns an unwrapped single error dict{"code": ..., "message": ...}which is not a valid ADCP error response on its own. def list_creatives_response(creatives: list[Any],
*,
pagination: dict[str, Any] | None = None,
sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def list_creatives_response( creatives: list[Any], *, pagination: dict[str, Any] | None = None, sandbox: bool = True, ) -> dict[str, Any]: """Build a list_creatives response. Each creative should include: creative_id, name, format_id, status. Matches ListCreativesResponse schema. Timestamp defaults: every Creative item in the spec requires ``created_date`` and ``updated_date`` (ISO 8601 UTC). For any dict item that omits either field, this helper fills it with the current UTC timestamp via :func:`_rfc3339_now` (Zulu suffix, RFC 3339). Both fields default to the same value when neither is provided, which matches the intuitive meaning for a freshly-listed item. Explicit caller-provided values are always preserved. Pydantic model items are passed through ``_serialize`` unchanged — callers using typed Creative models should set timestamps on the model. """ now = _rfc3339_now() filled: list[Any] = [] for item in creatives: if isinstance(item, dict): has_created = "created_date" in item and item["created_date"] is not None has_updated = "updated_date" in item and item["updated_date"] is not None if has_created and has_updated: filled.append(item) continue patched = dict(item) if not has_created: patched["created_date"] = now if not has_updated: patched["updated_date"] = now filled.append(patched) else: filled.append(item) count = len(filled) return { "creatives": _serialize(filled), "pagination": pagination or {"total_count": count, "has_more": False}, "query_summary": {"total_results": count, "total_matching": count, "returned": count}, "sandbox": sandbox, }Build a list_creatives response.
Each creative should include: creative_id, name, format_id, status. Matches ListCreativesResponse schema.
Timestamp defaults: every Creative item in the spec requires
created_dateandupdated_date(ISO 8601 UTC). For any dict item that omits either field, this helper fills it with the current UTC timestamp via :func:_rfc3339_now(Zulu suffix, RFC 3339). Both fields default to the same value when neither is provided, which matches the intuitive meaning for a freshly-listed item. Explicit caller-provided values are always preserved. Pydantic model items are passed through_serializeunchanged — callers using typed Creative models should set timestamps on the model. def log_event_response(events_received: int, events_processed: int, *, sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def log_event_response( events_received: int, events_processed: int, *, sandbox: bool = True, ) -> dict[str, Any]: """Build a log_event success response. Matches LogEventResponse1 (success) schema. """ return { "events_received": events_received, "events_processed": events_processed, "sandbox": sandbox, }Build a log_event success response.
Matches LogEventResponse1 (success) schema.
def media_buy_error_response(errors: list[dict[str, str]]) ‑> dict[str, typing.Any]-
Expand source code
def media_buy_error_response(errors: list[dict[str, str]]) -> dict[str, Any]: """Build a create_media_buy error response. Each error dict: {"code": "...", "message": "..."}. Matches CreateMediaBuyResponse2 (error) schema. """ return {"errors": errors}Build a create_media_buy error response.
Each error dict: {"code": "…", "message": "…"}. Matches CreateMediaBuyResponse2 (error) schema.
def media_buy_response(media_buy_id: str,
packages: list[Any],
*,
buyer_ref: str | None = None,
status: str | None = None,
valid_actions: list[str] | None = None,
revision: int | None = None,
confirmed_at: str | None | object = <object object>,
adcp_version: str | None = None,
sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def media_buy_response( media_buy_id: str, packages: list[Any], *, buyer_ref: str | None = None, status: str | None = None, valid_actions: list[str] | None = None, revision: int | None = None, confirmed_at: str | None | object = _UNSET, adcp_version: str | None = None, sandbox: bool = True, ) -> dict[str, Any]: """Build a create_media_buy success response. Each package should include: package_id, product_id, pricing_option_id, budget. Matches CreateMediaBuyResponse1 (success) schema. Auto-populates valid_actions from status if not provided. Auto-sets revision to 1 and confirmed_at to now if omitted. Pass ``confirmed_at=None`` explicitly only on AdCP 3.1+ shapes when the commitment timestamp is unavailable; pre-confirmation workflows such as IO signing or governance review should use the submitted task envelope rather than a synchronous media-buy success. Treat ``revision`` as the optimistic-concurrency token clients pass back on ``update_media_buy``. Treat ``confirmed_at`` as the seller commitment timestamp: once a buy is confirmed, pass the original value when rebuilding later create/get-style media-buy objects rather than stamping the current lifecycle transition time. ``confirmed_at=None`` is only schema-valid for AdCP 3.1+ response shapes; when ``adcp_version="3.0"`` is requested this helper raises ``ValueError`` rather than emitting 3.0-invalid ``null``. Pass ``adcp_version="3.0"`` for the 3.0 top-level lifecycle status shape, or an exact 3.1+ supported version for the task-envelope shape (``status="completed"`` plus ``media_buy_status``). When omitted, the dispatcher projects by the buyer's requested version. """ if adcp_version is not None and not _is_adcp_31_or_newer(adcp_version) and confirmed_at is None: raise ValueError("confirmed_at=None is not valid for AdCP 3.0 media_buy_response") resp: dict[str, Any] = { "media_buy_id": media_buy_id, "packages": _serialize(packages), "revision": revision if revision is not None else 1, "sandbox": sandbox, } if confirmed_at is _UNSET: resp["confirmed_at"] = _rfc3339_now() else: resp["confirmed_at"] = confirmed_at if buyer_ref is not None: resp["buyer_ref"] = buyer_ref if status is not None: if adcp_version is None or _is_adcp_31_or_newer(adcp_version): resp["media_buy_status"] = status else: resp["status"] = status if valid_actions is None: resp["valid_actions"] = valid_actions_for_status(status) else: resp["valid_actions"] = valid_actions elif valid_actions is not None: resp["valid_actions"] = valid_actions if adcp_version is not None and _is_adcp_31_or_newer(adcp_version): resp["status"] = "completed" return respBuild a create_media_buy success response.
Each package should include: package_id, product_id, pricing_option_id, budget. Matches CreateMediaBuyResponse1 (success) schema.
Auto-populates valid_actions from status if not provided. Auto-sets revision to 1 and confirmed_at to now if omitted. Pass
confirmed_at=Noneexplicitly only on AdCP 3.1+ shapes when the commitment timestamp is unavailable; pre-confirmation workflows such as IO signing or governance review should use the submitted task envelope rather than a synchronous media-buy success. Treatrevisionas the optimistic-concurrency token clients pass back onupdate_media_buy. Treatconfirmed_atas the seller commitment timestamp: once a buy is confirmed, pass the original value when rebuilding later create/get-style media-buy objects rather than stamping the current lifecycle transition time.confirmed_at=Noneis only schema-valid for AdCP 3.1+ response shapes; whenadcp_version="3.0"is requested this helper raisesValueErrorrather than emitting 3.0-invalidnull. Passadcp_version="3.0"for the 3.0 top-level lifecycle status shape, or an exact 3.1+ supported version for the task-envelope shape (status="completed"plusmedia_buy_status). When omitted, the dispatcher projects by the buyer's requested version. def media_buys_response(media_buys: list[Any], *, sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def media_buys_response( media_buys: list[Any], *, sandbox: bool = True, ) -> dict[str, Any]: """Build a get_media_buys response. Each media buy should include: media_buy_id, status, currency, packages. Matches GetMediaBuysResponse schema. """ return { "media_buys": _serialize(media_buys), "sandbox": sandbox, }Build a get_media_buys response.
Each media buy should include: media_buy_id, status, currency, packages. Matches GetMediaBuysResponse schema.
def preview_creative_response(previews: list[dict[str, Any]],
*,
expires_at: str | None = None,
sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def preview_creative_response( previews: list[dict[str, Any]], *, expires_at: str | None = None, sandbox: bool = True, ) -> dict[str, Any]: """Build a preview_creative single response. Each preview should include: preview_id, input ({format_id, name, assets}), renders ([{render_id, output_format, preview_url, role, dimensions}]). Matches PreviewCreativeResponse1 (single) schema. """ return { "response_type": "single", "previews": _serialize(previews), "expires_at": expires_at or "2099-12-31T23:59:59Z", "sandbox": sandbox, }Build a preview_creative single response.
Each preview should include: preview_id, input ({format_id, name, assets}), renders ([{render_id, output_format, preview_url, role, dimensions}]).
Matches PreviewCreativeResponse1 (single) schema.
def products_response(products: list[Any] | None = None,
*,
item_count: int | None = None,
proposals: list[Any] | None = None,
incomplete: list[Any] | None = None,
pagination: dict[str, Any] | None = None,
wholesale_feed_version: str | None = None,
pricing_version: str | None = None,
cache_scope: str | None = None,
unchanged: bool | None = None,
status: str = 'completed',
sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def products_response( products: list[Any] | None = None, *, item_count: int | None = None, proposals: list[Any] | None = None, incomplete: list[Any] | None = None, pagination: dict[str, Any] | None = None, wholesale_feed_version: str | None = None, pricing_version: str | None = None, cache_scope: str | None = None, unchanged: bool | None = None, status: str = "completed", sandbox: bool = True, ) -> dict[str, Any]: """Build a get_products response. Matches GetProductsResponse schema, including beta 3 wholesale feed metadata for cache/version-aware enumeration. Pass ``cache_scope`` explicitly for spec-valid wholesale responses; the dispatcher only infers ``public`` for request paths without an account. """ serialized = _serialize(products) if products is not None else None resp: dict[str, Any] = { "status": status, "sandbox": sandbox, } if serialized is not None: resp["products"] = serialized if item_count is not None: resp["item_count"] = item_count elif serialized is not None: resp["item_count"] = len(serialized) if proposals is not None: resp["proposals"] = _serialize(proposals) if incomplete is not None: resp["incomplete"] = _serialize(incomplete) if pagination is not None: resp["pagination"] = pagination if wholesale_feed_version is not None: resp["wholesale_feed_version"] = wholesale_feed_version if pricing_version is not None: resp["pricing_version"] = pricing_version if cache_scope is not None: resp["cache_scope"] = cache_scope if unchanged is not None: resp["unchanged"] = unchanged return respBuild a get_products response.
Matches GetProductsResponse schema, including beta 3 wholesale feed metadata for cache/version-aware enumeration. Pass
cache_scopeexplicitly for spec-valid wholesale responses; the dispatcher only inferspublicfor request paths without an account. def signals_response(signals: list[Any] | None = None,
*,
incomplete: list[Any] | None = None,
pagination: dict[str, Any] | None = None,
wholesale_feed_version: str | None = None,
pricing_version: str | None = None,
cache_scope: str | None = None,
unchanged: bool | None = None,
status: str = 'completed',
sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def signals_response( signals: list[Any] | None = None, *, incomplete: list[Any] | None = None, pagination: dict[str, Any] | None = None, wholesale_feed_version: str | None = None, pricing_version: str | None = None, cache_scope: str | None = None, unchanged: bool | None = None, status: str = "completed", sandbox: bool = True, ) -> dict[str, Any]: """Build a get_signals response. Each signal should include: signal_agent_segment_id, name, description, signal_type, data_provider, coverage_percentage, deployments, pricing_options, signal_id. Matches GetSignalsResponse schema, including beta 3 wholesale feed metadata for cache/version-aware enumeration. Pass ``cache_scope`` explicitly for spec-valid wholesale responses; the dispatcher only infers ``public`` for request paths without an account. """ resp: dict[str, Any] = {"status": status, "sandbox": sandbox} if signals is not None: resp["signals"] = _serialize(signals) if incomplete is not None: resp["incomplete"] = _serialize(incomplete) if pagination is not None: resp["pagination"] = pagination if wholesale_feed_version is not None: resp["wholesale_feed_version"] = wholesale_feed_version if pricing_version is not None: resp["pricing_version"] = pricing_version if cache_scope is not None: resp["cache_scope"] = cache_scope if unchanged is not None: resp["unchanged"] = unchanged return respBuild a get_signals response.
Each signal should include: signal_agent_segment_id, name, description, signal_type, data_provider, coverage_percentage, deployments, pricing_options, signal_id. Matches GetSignalsResponse schema, including beta 3 wholesale feed metadata for cache/version-aware enumeration. Pass
cache_scopeexplicitly for spec-valid wholesale responses; the dispatcher only inferspublicfor request paths without an account. def sync_accounts_response(accounts: list[dict[str, Any]], *, sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def sync_accounts_response( accounts: list[dict[str, Any]], *, sandbox: bool = True, ) -> dict[str, Any]: """Build a sync_accounts success response. Each account dict should include: account_id, brand, operator, action ("created"|"updated"), status ("active"|"pending_approval"). Matches SyncAccountsResponse1 schema (field: "accounts"). Items pass through :func:`_serialize` so loose-dict adopters who spread an input ``governance_agents`` (with ``authentication``) or ``billing_entity`` (with ``bank``) onto the response get the write-only credential strip. """ return {"accounts": _serialize(accounts), "sandbox": sandbox}Build a sync_accounts success response.
Each account dict should include: account_id, brand, operator, action ("created"|"updated"), status ("active"|"pending_approval").
Matches SyncAccountsResponse1 schema (field: "accounts").
Items pass through :func:
_serializeso loose-dict adopters who spread an inputgovernance_agents(withauthentication) orbilling_entity(withbank) onto the response get the write-only credential strip. def sync_catalogs_response(catalogs: list[dict[str, Any]], *, sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def sync_catalogs_response( catalogs: list[dict[str, Any]], *, sandbox: bool = True, ) -> dict[str, Any]: """Build a sync_catalogs success response. Each catalog should include: catalog_id, action, item_count, items_approved. Matches SyncCatalogsResponse1 (success) schema. """ return { "catalogs": catalogs, "sandbox": sandbox, }Build a sync_catalogs success response.
Each catalog should include: catalog_id, action, item_count, items_approved. Matches SyncCatalogsResponse1 (success) schema.
def sync_creatives_response(creatives: list[dict[str, Any]], *, sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def sync_creatives_response( creatives: list[dict[str, Any]], *, sandbox: bool = True, ) -> dict[str, Any]: """Build a sync_creatives success response. Each creative dict should include: creative_id, action ("created"|"updated"). Optionally: status ("processing"|"pending_review"|"approved"|"rejected"|"archived"). Matches SyncCreativesResponse1 schema (field: "creatives"). """ return {"creatives": _serialize(creatives), "sandbox": sandbox}Build a sync_creatives success response.
Each creative dict should include: creative_id, action ("created"|"updated"). Optionally: status ("processing"|"pending_review"|"approved"|"rejected"|"archived"). Matches SyncCreativesResponse1 schema (field: "creatives").
def sync_governance_response(accounts: list[dict[str, Any]], *, sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def sync_governance_response( accounts: list[dict[str, Any]], *, sandbox: bool = True, ) -> dict[str, Any]: """Build a sync_governance response. Each account dict should include: account, status ("synced"), governance_agents ([{url, categories}]). Items pass through :func:`_serialize` so loose-dict adopters who spread an input ``governance_agents`` (with ``authentication``) onto the response get the write-only credential strip. """ return {"accounts": _serialize(accounts), "sandbox": sandbox}Build a sync_governance response.
Each account dict should include: account, status ("synced"), governance_agents ([{url, categories}]).
Items pass through :func:
_serializeso loose-dict adopters who spread an inputgovernance_agents(withauthentication) onto the response get the write-only credential strip. def update_media_buy_response(media_buy_id: str,
*,
affected_packages: list[Any] | None = None,
status: str | None = None,
valid_actions: list[str] | None = None,
revision: int | None = None,
adcp_version: str | None = None,
sandbox: bool = True) ‑> dict[str, typing.Any]-
Expand source code
def update_media_buy_response( media_buy_id: str, *, affected_packages: list[Any] | None = None, status: str | None = None, valid_actions: list[str] | None = None, revision: int | None = None, adcp_version: str | None = None, sandbox: bool = True, ) -> dict[str, Any]: """Build an update_media_buy success response. Matches UpdateMediaBuyResponse1 (success) schema. Auto-populates valid_actions from status if not provided. ``revision`` is the new optimistic-concurrency token after the update; clients should use it on their next mutating ``update_media_buy`` call. Current AdCP response shapes require ``revision``. Only explicit pre-3.1 compatibility output may omit it. Pass ``adcp_version="3.0"`` for the 3.0 top-level lifecycle status shape, or an exact 3.1+ supported version for the task-envelope shape (``status="completed"`` plus ``media_buy_status``). When omitted, the dispatcher projects by the buyer's requested version. """ if (adcp_version is None or _is_adcp_31_or_newer(adcp_version)) and revision is None: raise ValueError("revision is required for AdCP 3.1+ update_media_buy_response") resp: dict[str, Any] = { "media_buy_id": media_buy_id, "sandbox": sandbox, } if revision is not None: resp["revision"] = revision if affected_packages is not None: resp["affected_packages"] = _serialize(affected_packages) if status is not None: if adcp_version is None or _is_adcp_31_or_newer(adcp_version): resp["media_buy_status"] = status else: resp["status"] = status if valid_actions is None: resp["valid_actions"] = valid_actions_for_status(status) else: resp["valid_actions"] = valid_actions elif valid_actions is not None: resp["valid_actions"] = valid_actions if adcp_version is not None and _is_adcp_31_or_newer(adcp_version): resp["status"] = "completed" return respBuild an update_media_buy success response.
Matches UpdateMediaBuyResponse1 (success) schema. Auto-populates valid_actions from status if not provided.
revisionis the new optimistic-concurrency token after the update; clients should use it on their next mutatingupdate_media_buycall. Current AdCP response shapes requirerevision. Only explicit pre-3.1 compatibility output may omit it. Passadcp_version="3.0"for the 3.0 top-level lifecycle status shape, or an exact 3.1+ supported version for the task-envelope shape (status="completed"plusmedia_buy_status). When omitted, the dispatcher projects by the buyer's requested version.