Identity model
Implementations MUST retain each identity in its own namespace and MUST NOT substitute one identity for another.
An SDK MAY present a composite operation object containing several of these
values. That convenience object does not merge their semantics.
Stable seller-work identity
Once an operation is bound to an AdCPtask_id, every later AdCP observation
of that operation—submitted, subsequent status reads, paused states,
terminal results, task webhooks, and reconciliation—MUST repeat that value or
inherit it from an envelope whose binding is unambiguous. A different explicit
AdCP task_id for the same operation is a protocol conflict. A client MUST NOT
silently rebind the operation or treat the new value as a successor.
An A2A transport Task can complete with an AdCP Submitted result. In that case,
the A2A Task.id identifies the completed transport invocation and the AdCP
task_id identifies the continuing seller work. Polling uses only the AdCP
handle. Native A2A continuation uses only the A2A Task and context identifiers.
One operation, competing observations
Direct responses, AdCP polling results, continuation responses, and authenticated webhooks are competing observations of one logical operation. They are not independent state machines. An observation is authoritative only after the receiver has:- authenticated its source;
- validated its envelope and task-specific payload;
- matched the operation, account, task type, and all bound identities; and
- verified that the observation is permitted from the operation’s current state.
- an exact terminal duplicate is acknowledged and does not republish effects;
- a non-terminal observation after terminal settlement is acknowledged as stale and does not reopen the operation; and
- a different terminal payload or terminal status is a protocol conflict and MUST NOT replace, merge with, or republish over the winner.
completed, failed, canceled, and rejected are terminal. working,
submitted, input-required, and auth-required are non-terminal. None can
displace an accepted terminal result. unknown is indeterminate and is not an
authoritative terminal observation: the receiver MUST reconcile it, and it
cannot settle or reopen an operation. A terminal webhook MAY arrive before the
originating request returns working or submitted; this is an allowed race,
not an unknown-task error. The implementation preserves the validated terminal
observation and converges the later response onto it.
Terminal identity and equivalence
For asynchronous work already bound to a seller task, one terminal settlement is identified by(authenticated seller identity, AdCP task_id). task_type,
operation, account, and principal bindings are validated constraints on that
identity. A mismatch is a correlation conflict, not a second settlement.
Synchronous inline terminal responses have no AdCP task identity and cannot
race a task webhook: 3.x sellers MUST NOT synthesize a task webhook for an
inline terminal response.
Channels wrap the task result differently. Before duplicate comparison, a
receiver extracts the task-specific terminal value:
The canonical comparison record is RFC 8785 JCS of
{task_type, status, value}. Transport-only observation fields—including A2A Task and context
identifiers, webhook idempotency_key, notification_id, operation_id,
AdCP task_id, delivery timestamp, envelope message, and replayed—are not
part of value. A field declared by the task-specific response schema remains
part of value, including task-body context or errors when declared.
When both a terminal result and the polling convenience error are present,
the error MUST agree with the canonical fatal error in result. A legacy 3.x
poll that exposes only singular error can establish that the task failed, but
cannot prove that a later full terminal artifact is an exact duplicate. The
receiver preserves the failure fence and reconciles the complete artifact; it
does not publish from the incomplete observation or call a different payload
equivalent. More generally, when a channel cannot be validated or normalized
to the selected task schema, it is not authoritative for publication.
Normative race table
“Acknowledge” below means return the success form appropriate to the channel. For an authenticated HTTP webhook, that is a2xx response.
Webhook delivery-key binding
A webhook publisher MUST bind(authenticated sender identity, idempotency_key) to the complete authenticated payload’s RFC 8785 JSON
Canonicalization Scheme (JCS) form for the advertised retry window. Duplicate
object keys MUST be rejected before canonicalization. It reuses that key only
for JCS-equivalent retries of that delivery. A re-emission of the same logical
notification with a changed
envelope or payload uses a fresh delivery key and retains the same
notification_id when that event type defines one.
A receiver MUST establish the same immutable binding before application side
effects. It fingerprints the authenticated canonical payload and stores that
fingerprint with the delivery claim. The three outcomes are distinct:
The delivery key prevents duplicate transport processing. The stable terminal
observation identity prevents duplicate terminal publication across distinct
deliveries. Implementations need both protections.
Resumability and continuation generations
input-required and auth-required describe why work is paused. They do not,
by themselves, prove that the interaction can be resumed.
An SDK or transport exposes a resume operation only when it has a verified
continuation descriptor containing the native protocol and transport identity
required for that continuation. For A2A, that includes the native A2A Task ID
and context ID. An AdCP task_id from get_task_status is only a polling
handle and MUST NOT be copied into a missing A2A Task ID or otherwise used to
fabricate a continuation.
AdCP 3.2 defines no transport-neutral continuation request or generic
continuation descriptor in task-body schemas. Application-layer
input-required and auth-required responses are therefore resumable only
when the active transport independently supplies its native verified resume
identity, such as A2A Task.id plus contextId. Standardizing a portable
descriptor without a matching redemption method would create an unusable
credential; that wire surface is deferred to AdCP 4.0.
If a transport did not return enough verified identity to continue, the client
fails closed and directs the caller to polling, reconciliation, or a new
operation as allowed by the task. A context_id or status label alone does not
authorize a resume.
Continuation tokens are opaque bearer capabilities. They MUST:
- contain at least 128 bits of cryptographically random entropy;
- be bound to the authenticated principal, operation, transport identity, and one continuation generation;
- be single-generation and expire after a bounded interval;
- never appear in logs, analytics, URLs, error messages, or telemetry; and
- be compared and stored using the same protections as other bearer secrets.
Publication and durable acknowledgement
Deduplication is not complete when a receiver checks a marker and plans to write an acknowledgement later. Before invoking an adopter callback, billing hook, fulfillment handler, or other externally visible side effect, the receiver MUST durably establish a single publication owner for that terminal observation. After successful publication it records durable acknowledgement. Exact duplicates then return success without invoking the handler. If publication fails, ownership can be released or expire under a bounded recovery policy. If a crash makes the external side effect uncertain, the receiver reconciles that side effect or preserves the uncertainty fence; it MUST NOT assume failure and give a second publisher unrestricted ownership.Retry horizons
An agent’s capability response advertises the request idempotency replay window and, when it emits webhooks, its maximum webhook delivery retry window aswebhook_signing.delivery_retry_horizon_seconds.
These are guaranteed floors for retained replay evidence, not suggestions to
keep retrying until the last second.
Receivers retain the immutable webhook delivery-key-to-payload binding and
terminal publication proof for at least the greater of 24 hours and the
sender’s advertised retry window.
When no webhook retry window is advertised by a 3.x peer, both sides use the
3.x compatibility floor of 24 hours. A publisher MUST NOT retry after the
window it advertised for the delivery.
Retention is measured from the first accepted request or delivery. Extending
retention on every retry would allow a hostile or broken peer to keep state
alive indefinitely. Clock-skew tolerance cannot shorten the advertised
window.
Composite and compatibility operations
Splitting one legacy task into multiple compact tasks changes transaction and failure boundaries. A compatibility facade is lossless only if it preserves the legacy task’s validation and atomic-commit semantics, not merely its final field values. For example, a legacycreate_media_buy can include packages, creatives, and
assignments in one request. Structural, reference, and routing validation of
those inline creatives occurs before commit, so one such validation failure
rejects the request without creating the buy. Later asynchronous policy or
human review can still reject a creative without rolling back an already valid
buy. A facade implemented as compact purchase followed by creative sync moves
the pre-commit validation boundary and can commit spend before it discovers a
structural or routing failure. That sequence is not a lossless implementation.
A compatibility facade therefore either:
- validates and commits every component through one private all-or-none boundary; or
- rejects before the first mutation.
Implementation checklist
For every asynchronous or compatibility workflow, document and test:- every identity and its issuing authority;
- the point at which seller-work identity becomes stable;
- the canonical terminal payload and conflict comparison;
- publication ownership before external effects and acknowledgement after;
- continuation generation, supersession, and crash discovery;
- advertised request and webhook retry horizons;
- every mutation boundary and possible partial-success state; and
- recovery after a crash at each boundary.