Documentation Index
Fetch the complete documentation index at: https://agenticadvertisingorg-changeset-release-main.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Calling an AdCP agent
This page is the canonical buyer-side wire contract: the rules that don’t live cleanly in any single task schema, but apply to every mutating call you’ll make. If you’re building a buyer (DSP, planning tool, agentic client) and calling out to AdCP sales, creative, signals, governance, SI, or brand agents, read this once. The agent-facing version of this content lives atskills/call-adcp-agent/SKILL.md — bundled into the protocol tarball so SDKs can ship it to coding agents.
Discovery chain
Walk these in order on first contact with any new agent:- Agent card (A2A) or
tools/list(MCP): returns tool names. AdCP MCP servers no longer publish per-tool parameter schemas intools/list— every tool shows{type: 'object', properties: {}}. Don’t try to infer shape from there. get_adcp_capabilities: returns supported protocols, AdCP major versions, and feature flags. Tells you which tools this agent supports, not how to call them. Seeget_adcp_capabilities.get_schema(tool_name)(when the agent exposes it — pending standardization, see #3057): returns the JSON Schema for a specific tool’s request/response.- Bundled schemas (offline, authoritative): every published AdCP version ships JSON Schemas for every tool, signed via Sigstore. The path differs by SDK — the spec repo source uses
dist/schemas/<version>/bundled/,@adcp/sdkputs them atschemas/cache/<version>/bundled/afternpm run sync-schemas, Python and Go SDKs use their own conventions. Don’t hardcode a path; let the SDK’s loader find them. Once located, each schema lives at<protocol>/<tool>-{request,response}.json.
Idempotency: replay vs. new operation
Every mutating tool requires anidempotency_key (UUID).
- Same key on retry → server replays the same response, byte-for-byte. Use this for transport-level retries (timeout, 5xx, dropped connection).
- Fresh key → new operation, regardless of body. Generating a new UUID because the previous attempt failed is the most common way naïve callers create duplicate media buys.
- Same key, different canonical body →
IDEMPOTENCY_CONFLICT. Sellers MUST reject (rule 5 in security.mdx#idempotency) — do not silently apply the second body, do not silently replay the first response. - Same key while first request still running →
IDEMPOTENCY_IN_FLIGHT(rule 9 in security.mdx#idempotency). The seller MAY return this code witherror.details.retry_afterinstead of blocking. Wait and retry with the same key — minting a fresh key on this code turns a safe retry into a double-execution race.
task_id so polling continues against the same task instead of forking.
idempotency_key is required on: create_media_buy, update_media_buy, sync_creatives, sync_audiences, sync_accounts, sync_catalogs, sync_event_sources, sync_plans, sync_governance, activate_signal, acquire_rights, log_event, report_usage, provide_performance_feedback, report_plan_outcome, create_property_list, update_property_list, delete_property_list, create_collection_list, update_collection_list, delete_collection_list, create_content_standards, update_content_standards, calibrate_content, si_initiate_session, si_send_message.
Missing the key → adcp_error.code: 'VALIDATION_ERROR' with /idempotency_key in issues.
account is oneOf — pick exactly one variant
account is a discriminated union. On create_media_buy and update_media_buy, two variants:
additionalProperties: false on each variant means {account_id, brand} fails BOTH.
Other tools (e.g. sync_creatives) may accept a superset — always check the specific tool’s schema.
Async responses: status: 'submitted' means queued
A mutating tool can return one of three shapes:
status: 'submitted', the work is not complete. Poll via tasks/get (A2A) or the MCP async task extension, using the returned task_id. Over A2A the AdCP task_id also rides on artifact.metadata.adcp_task_id.
Pass include_result: true when polling so the seller includes the completion payload once status transitions to completed:
result field uses the same payload structure as the push-notification webhook result field for completed tasks — buyers who configure both polling and webhooks receive the same data shape either way.
Error recovery — read issues[]
Every validation failure produces an envelope shaped like:
issues[].pointer— RFC 6901 JSON Pointer to the offending fieldissues[].keyword— Ajv keyword (required,type,oneOf,anyOf,additionalProperties,format,enum)issues[].variants— whenkeywordisoneOforanyOf, each entry lists one variant’srequired+ declaredproperties
oneOf failures, pick ONE variant from variants[] and send only its required fields. This is the fastest recovery path when you didn’t know the field was a union.
recovery values:
correctable— buyer-side fix; readissues[], patch the pointers, resendtransient— retry with the sameidempotency_keyterminal— requires human action (account suspended, payment required); do not retry
Common shape pitfalls
| Symptom | What it means | Fix |
|---|---|---|
keyword: 'oneOf' with variants[] | Discriminated union — you sent fields from multiple variants, or none | Pick ONE variant from variants[]. Send only its required fields. |
2-3 additionalProperties errors at the same pointer | You merged oneOf variants | Drop to one variant. Don’t keep “extra” fields “for completeness”. |
keyword: 'required', pointer: '/idempotency_key' | Mutating tool, no UUID | Generate fresh UUID per logical operation. Reuse on retries. |
keyword: 'type' or additionalProperties at /budget | Sent {amount, currency} | budget is a number. Currency is implied by pricing_option_id. |
additionalProperties at /format_id (string passed) | Sent "format_id": "video_..." | format_id is {agent_url, id} — always an object. |
keyword: 'enum' at /destinations/*/type | Made-up destination type | Use 'platform' (with platform) or 'agent' (with agent_url). |
Response carries status: 'submitted' and task_id | Async — work is queued, NOT done | Poll via tasks/get (A2A) or the MCP async task extension using task_id. |
Transport notes
- MCP:
tools/callwith{ name: 'tool_name', arguments: {...} }. ReadstructuredContentfor the typed response. - A2A:
message/sendwith aDataPartof shape{ skill: 'tool_name', input: {...} }. The typed response is attask.artifacts[0].parts[0].data.
Task.state: 'completed' is not the same as AdCP completion. A2A task state describes the transport call lifecycle; AdCP-level completion is in the artifact’s payload (structuredContent.status or data.status). A completed A2A task can still carry a submitted AdCP response.
Related
- Per-task request/response shapes: see the protocol-specific reference (
/docs/media-buy/,/docs/creative/,/docs/signals/, etc.). - Protocol architecture — how the protocol domains fit together.
- Required tasks — which tasks an agent must implement to claim a specialism.
get_adcp_capabilities— first call against any new agent.- Schemas — how SDKs consume the protocol tarball (which now bundles
skills/). - Build a caller — build-shaped guide for the caller side: install, call, handle responses, ingest reporting.