Required Structure
Final Responses (status: “completed”)
AdCP responses over A2A MUST:- Include at least one
DataPart(kind: ‘data’) containing the task response payload - Use single artifact with multiple parts (not multiple artifacts)
- Use the last
DataPartas authoritative when multiple data parts exist - NOT wrap AdCP payloads in custom framework objects (no
{ response: {...} }wrappers)
- TextPart (kind: ‘text’): Human-readable summary - recommended but optional
- DataPart (kind: ‘data’): Structured AdCP response payload - required
- FilePart (kind: ‘file’): Optional file references (previews, reports)
Interim Responses (working, submitted, input-required)
Interim status responses can include optional AdCP-structured data for progress tracking.- TextPart is recommended for human-readable status
- DataPart is optional but follows AdCP schemas when provided
- Interim status schemas (
*-async-response-working.json,*-async-response-input-required.json, etc.) are work-in-progress and may evolve - Implementors may choose to handle interim data more loosely given schema evolution
status: "completed" or status: "failed"), DataPart with full AdCP task response becomes required in .artifacts.
Framework Wrappers (NOT PERMITTED)
CRITICAL: DataPart content MUST be the direct AdCP response payload, not wrapped in framework-specific objects.- Breaks schema validation (clients expect
productsat root, notresponse.products) - Adds unnecessary nesting layer
- Violates protocol-agnostic design (wrapper is framework-specific)
- Complicates client extraction code
Canonical Client Behavior
This section defines EXACTLY how clients MUST extract AdCP responses from A2A protocol responses.Quick Reference
| Status | Webhook Type | Data Location | Schema Required? | Returns |
|---|---|---|---|---|
working | TaskStatusUpdateEvent | status.message.parts[] | ✅ Yes (if present) | { status, taskId, message, data? } |
submitted | TaskStatusUpdateEvent | status.message.parts[] | ✅ Yes (if present) | { status, taskId, message, data? } |
input-required | TaskStatusUpdateEvent | status.message.parts[] | ✅ Yes (if present) | { status, taskId, message, data? } |
completed | Task | .artifacts[] only | ✅ Required | { status, taskId, message, data } |
failed | Task | .artifacts[] only | ✅ Required | { status, taskId, message, data } |
- Final statuses use
Taskobject with data in.artifacts - Interim statuses use
TaskStatusUpdateEventwith optional data instatus.message.parts[] - All statuses use AdCP schemas when data is present
- Interim status schemas are work-in-progress and may evolve
Rule 1: Status-Based Handling
Clients MUST branch onstatus field to determine the correct data extraction location:
- Interim statuses use
TaskStatusUpdateEvent→ extract fromstatus.message.parts[] - Final statuses use
Taskobject → extract from.artifacts[0].parts[]
Rule 2: Data Extraction Helpers
Extract data from the appropriate location based on webhook type:Rule 3: Schema Validation
All AdCP responses use schemas, but validation approach varies by status:*-async-response-working.json, etc.) are work-in-progress. Implementors may choose to handle these more loosely while schemas stabilize.
Complete Example
Putting it all together with proper handling of both Task and TaskStatusUpdateEvent payloads:Last Data Part Authority Pattern
Test Cases
✅ Correct Behavior
❌ Incorrect Behavior (Common Mistakes)
Error Handling
Task-Level Errors (Partial Failures)
Task executed but couldn’t complete fully. Useerrors array in DataPart with status: "completed":
- Platform authorization issues (
PLATFORM_UNAUTHORIZED) - Partial data availability
- Validation issues in subset of data
Protocol-Level Errors (Fatal)
Task couldn’t execute. Usestatus: "failed" with message:
- Authentication failures (invalid credentials, expired tokens)
- Invalid request parameters (malformed JSON, missing required fields)
- Resource not found (unknown taskId, expired context)
- System errors (database unavailable, internal service failure)
Status Mapping
AdCP uses A2A’s TaskState enum directly:| A2A Status | Payload Type | Data Location | AdCP Usage |
|---|---|---|---|
completed | Task | .artifacts | Task finished successfully, data in DataPart, optional errors array |
failed | Task | .artifacts | Fatal error preventing completion, optional error details |
input-required | TaskStatusUpdateEvent | status.message.parts | Need user input/approval, data + text explaining what’s needed |
working | TaskStatusUpdateEvent | status.message.parts | Processing (< 120s), optional progress data |
submitted | TaskStatusUpdateEvent | status.message.parts | Long-running (hours/days), minimal data, use webhooks/polling |
Webhook Payloads
Async operations (status: "submitted") deliver the same artifact structure in webhooks:
File Parts in Responses
Creative operations MAY include file references:Retry and Idempotency
TaskId-Based Deduplication
A2A’staskId enables retry detection. Agents SHOULD:
- Return cached response if
taskIdmatches a completed operation (within TTL window) - Reject duplicate
taskIdsubmission if operation is still in progress
Examples
Implementation Checklist
When implementing A2A responses for AdCP: Final Responses (status: “completed” or “failed”) - UseTask object:
- Always include status field from TaskState enum
- Use
.artifactsarray with at least one DataPart containing AdCP response payload - Include TextPart with human-readable message (recommended for UX)
- Use single artifact with multiple parts (not multiple artifacts)
- Use last DataPart as authoritative if multiple exist
- Never nest AdCP data in custom wrappers (no
{ response: {...} }objects) - DataPart content MUST match AdCP schemas (validate against
[task]-response.json)
TaskStatusUpdateEvent:
- Use
status.message.parts[]for optional data (not.artifacts) - TextPart is recommended for human-readable status updates
- DataPart is optional but follows AdCP schemas when provided (
[task]-async-response-[status].json) - Interim schemas are work-in-progress - clients may handle more loosely
- Include progress indicators when applicable (percentage, current_step, ETA)
- Use
status: "failed"for protocol errors only (auth, invalid params, system errors) - Use
errorsarray for task failures (platform auth, partial data) withstatus: "completed"
- Include taskId and contextId for tracking
- Follow discriminated union patterns for task responses (check schemas)
- Use correct payload type:
Taskfor final states,TaskStatusUpdateEventfor interim - Support taskId-based deduplication for retry detection
See Also
- A2A Guide - Complete A2A integration guide
- Core Concepts - Status handling patterns
- Error Handling - Fatal vs non-fatal errors
- Protocol Comparison - MCP vs A2A differences