Skip to main content
Every AdCP response includes a status field that tells you exactly what state the operation is in and what action you should take next. This is the foundation for handling any AdCP operation. :::note Application-layer task state The status values and lifecycle described here are transport-independent AdCP application state. MCP and A2A task mechanisms may wrap, stream, or deliver an AdCP response, but they do not replace AdCP’s task_id, webhook payloads, or polling/reconciliation surfaces. For submitted operations, observe the AdCP task with push notifications or the AdCP polling surface. In 3.x, that polling surface is legacy tasks/get, with optional get_task_status when the seller advertises the alias. Transport-native MCP/A2A tasks/* methods use their own wire shapes and are separate from AdCP task polling. :::

Status Values

AdCP uses the same status values as the A2A protocol’s TaskState enum:

Response Structure

Every AdCP response uses a flat structure where task-specific fields are at the top level:
:::warning Single status field required Agents MUST NOT emit the legacy task_status or response_status fields alongside status. The status field is the single authoritative task state. Agents emitting either alongside status are non-conformant. :::

Status Handling

Basic Pattern

Clarification Flow

When status is input-required, the message tells you what’s needed:
Client handling:

Approval Flow

Human approval at the task layer is modelled as input-required (when the buyer must respond, e.g. confirm a budget) or submitted (when the seller is waiting on an internal human, e.g. IO signing). These implement the Embedded Human Judgment principle that judgment cannot be delegated to software — when an action exceeds autonomous authority, the system halts for human review rather than proceeding.
pending_approval is an Account status, not a task status and not a MediaBuy status. It indicates the seller is reviewing an account (credit, contracts) before it can be used. Don’t reuse the name for task-level approval.
Client handling:

Operations Over 30 Seconds

Operations that take longer than 30 seconds return either working or submitted. These statuses mean different things:
  • working: The server is actively processing and will deliver the result when ready. No polling needed — the server sends progress out-of-band and the result arrives on the open connection.
  • submitted: The operation is blocked on an external dependency (human approval, publisher review). Configure a webhook or poll.
Handling for submitted operations:
  • All transports: Use push notifications (recommended) or poll the AdCP task with get_task_status / legacy tasks/get.
  • MCP/A2A transport tasks: Treat native task state as transport state. A native task can finish once it has delivered an AdCP payload that still says status: 'submitted'.

Status Progression

Tasks progress through predictable states:
  • submitted: Task queued, blocked on external dependency — configure webhook or poll
  • working: Agent actively processing (>30s) — wait for result, no polling needed
  • input-required: Need user input, continue conversation
  • completed: Success, process results
  • failed: Error, handle appropriately

Polling and Timeouts

Polling is for submitted only

Don’t poll for working — the server delivers the result on the open connection. Polling is a backup for submitted operations (webhooks are preferred). Send include_result: true to receive the terminal task payload on the polled response once the task reaches status: completed. The result object on the response carries the same shape the original task would have returned synchronously — for example, polling a create_media_buy task returns result: { media_buy_id, packages, status }. For failed tasks, read the existing error field instead. Webhooks remain the supported delivery mechanism (see Push Notifications); include_result is the typed polling alternative for buyers that prefer pull over push.

Typed task results in SDKs

AdCP 3.2 deliberately keeps the polling envelope’s result property generic. Draft-07 cannot select a $ref dynamically from the sibling task_type value without enumerating every task, and embedding that enumeration made get_task_status grow whenever any async-capable tool grew. Exact result validation is still required; it is selected through the release manifest instead of duplicated into the polling schema. The release manifest.json artifact therefore publishes task_result_resolution. For a terminal result, first check terminal_schema_overrides[task_type]; when no override exists, replace {task_type} in /tools/{task_type}/response_schema. A tool’s canonical response schema already contains its task-local terminal branches, including the structured get_products rejection. SDK generators SHOULD use that rule to expose a generic relationship equivalent to:
A tool call that returns submitted can retain its tool name in TaskHandle<T>, so a later getTaskStatus(handle) returns the precise result type without importing a global union. SDKs SHOULD keep a string-only overload for recovered or persisted task IDs; that overload returns an unknown result until the response’s task_type is checked, then resolves and validates the matching manifest schema or override. If a typed handle’s expected tool differs from the returned task_type, the SDK MUST reject the response as a task-correlation failure. Webhook envelopes retain their separate async payload schema. For 3.x compatibility, task reconciliation also continues to accept the historically shared event value media_buy_delivery; its manifest override selects media-buy-delivery-webhook-result.json. AdCP 4.0 can separate event types from tool task types, remove the legacy tasks/get alias, and make this generic SDK relationship part of the canonical task model.

Timeout Configuration

Task Reconciliation

Use list_tasks (or legacy tasks/list in 3.x) to recover from lost state:

Best Practices

  1. Always check status first - Don’t assume success
  2. Handle all statuses - Include a default case for unknown states
  3. Preserve context_id - Required for conversation continuity
  4. Use task_id for tracking - Especially for long-running operations
  5. Implement timeouts - Don’t wait forever
  6. Log status transitions - Helps with debugging and auditing

Next Steps

  • Async Operations: See Async Operations for handling different operation types
  • Webhooks: See Webhooks for push notification patterns
  • Error Handling: See Error Handling for error categories and recovery