Agent interoperability · Analysis

A2A tasks vs messages: choosing the right lifecycle

A2A protocol tasks vs messages difference, when to use each, how to handle input and auth pauses, and where durable artifacts belong.

One A2A message branches into either an immediate reply or a tracked task that moves through states and produces an artifact
The short answer

In A2A, every interaction starts with a Message, but the remote agent can return either a direct Message or a Task. Return a Message when the response is immediate, self-contained, and needs no durable work record. Create a Task when the work needs status, additional input, authorization, cancellation, reconnection, or artifacts. A contextId can group either response type, while a taskId identifies one server-owned unit of work.

The A2A tasks vs messages decision is about state ownership. A client always sends a Message to an A2A server. The server then returns either another Message, which ends that exchange without creating a task resource, or a Task, which gives the work an identity and lifecycle.

That choice affects more than response shape. A task can be retrieved, listed, canceled, resumed after a connection failure, paused for input or authorization, and updated with artifacts. A direct response has none of those task operations. It can still belong to a continuing context, so calling it "stateless" does not mean the agent must forget the conversation.

Return a direct Message when the answer itself completes the interaction. Create a Task when the client needs to observe or influence work after acceptance. This guide applies that boundary to the A2A 1.0 contract.

One request, two response types

The A2A 1.0 specification defines SendMessage as the primary operation for starting or continuing an agent interaction. Its request contains a Message. Its response is a union: the remote agent returns a Task or a direct Message.

  • A Message is always the communication envelope sent by a client or agent.
  • A direct Message response is one possible outcome of SendMessage.
  • A Task is a server-created resource that tracks work caused by a message.

Suppose a client asks a travel agent, "Which currencies do you support?" The remote agent can answer with a direct Message because the reply is immediate and no tracked work remains. If the client says, "Build an accessible five-day itinerary and attach the bookings for approval," the agent should normally create a Task. The client now has reasons to check progress, provide missing dates, approve a purchase, cancel the work, or retrieve final files later.

The amount of model computation is not a reliable boundary. A short answer could require an expensive internal lookup, yet still complete before the server responds. A simple operation could need a Task because it depends on delayed human approval. Choose based on the external lifecycle the client needs, not the number of model calls or tools hidden inside the remote agent.

The response type is also separate from transport waiting. In A2A 1.0, SendMessageConfiguration.returnImmediately controls whether a non-streaming call waits for a Task to reach an interrupted or terminal state. It has no effect on a direct Message. A server can therefore return a Task that is already completed, or return an in-progress Task immediately. Both are still Tasks because both create a durable unit of work.

Request outcome Response object What exists afterward
Immediate, self-contained answer Message A communication turn, optionally grouped by contextId
Trackable work Task A server-owned resource with taskId, status, optional history, and artifacts
Quick but auditable operation Usually Task in a terminal state A durable record even though processing finished before the response
Clarification before accepting work Message Negotiation can continue in the same context without a task yet

Messages, tasks, artifacts, and identifiers

Integration problems start when Message, Task, and Artifact are treated as interchangeable response containers. Each object has a separate job.

Message: one communication turn

A Message carries a role, one or more content parts, and a creator-generated messageId. It may also carry a contextId, a taskId, or references to earlier tasks. Clients use messages to initiate work, answer a request for input, refine active work, or start related work. Agents use them for direct answers, clarification, and status explanations.

The messageId belongs to the message creator. The specification allows an agent to use it to detect duplicate SendMessage operations, but does not require every send operation to be idempotent. A client should preserve the identifier when retrying the same logical message rather than minting a new one for every network attempt.

Task: one stateful unit of work

A Task has a server-generated id, often called taskId in surrounding operations, plus current status, optional artifacts, optional message history, and metadata. The server creates the ID when it accepts new work. A client cannot choose a fresh task ID and ask the server to create that task.

When a client includes a taskId in a later Message, it must refer to an existing task. If it includes both taskId and contextId, the identifiers must match the same task. A server should reject a stale, inaccessible, or mismatched task reference instead of silently starting unrelated work.

Artifact: the output of a task

Artifacts hold task results such as a report, an image, structured records, or a file reference. Like Messages, they contain Parts, but their role is different. The current specification says task results should be returned as Artifacts, not hidden in status Messages.

That distinction matters after a disconnect. Status messages observed on a stream are not guaranteed to be replayed or persisted in task history. A critical result stored only in "Done, download the report here" may disappear from the client's view. Put durable output in the Task's artifacts and treat status messages as communication about the work.

contextId: a conversation, not a job

A contextId groups related independent Messages and Tasks. One context can contain a direct answer, two concurrent tasks, and a follow-up task that refines an earlier artifact. It is the broader conversational scope.

A taskId is narrower. It identifies one lifecycle. If a client wants a new revision after a task has completed, it starts a new interaction in the same contextId and can name the older task in referenceTaskIds. It does not reopen the completed task.

Identifier Creator Scope Retry or follow-up use
messageId Message sender One communication turn Reuse for a retry of the same logical send
contextId Usually the server for a new interaction Related Messages and Tasks Reuse to continue the same conversational context
taskId Server One stateful unit of work Reuse only while addressing that existing task
artifactId Producing agent One task output Use to assemble, replace, or identify output according to the event contract

When to return a direct Message

A direct Message fits when all material work finishes in the request-response exchange and neither party needs a task resource afterward. The official Life of a Task guide describes this path as an immediate, self-contained interaction without further state management.

Good candidates include capability negotiation, clarification before the server accepts a job, a quick answer that does not produce a managed artifact, and a refusal that occurs before any task is created. A support agent might answer "I can analyze PDF and CSV files, but not executable archives" directly. A research agent might ask which market and date range the client wants before accepting a report task.

Use a direct Message when these conditions hold:

  1. The response completes the requested interaction.
  2. The client does not need to poll, subscribe, cancel, or receive a webhook.
  3. No additional input or authorization pause is expected after acceptance.
  4. The response does not need a durable task-to-artifact record.
  5. Retrying the exchange can be handled at the message or application layer.

A direct Message can still include a server-issued contextId. That lets the client ask a related question later without creating a Task for every conversational turn. Message-only agents can therefore support coherent multi-turn conversations. What they do not expose is the formal lifecycle of a unit of work.

Do not select the direct path solely because work is fast. Consider an agent that applies a production configuration change in 200 milliseconds. The work may be quick, but operators may need a durable ID, authorization checkpoint, cancellation policy, artifact containing the applied diff, and an audit link to the side effect. A Task is a better semantic fit even if it reaches TASK_STATE_COMPLETED before the response returns.

There is also a product consistency argument for always returning completed Tasks. The official guide permits that design. It saves clients from handling two result variants, but it creates task records for greetings, capability questions, and other trivial replies. That cost is real: the server must define retention, listing, access control, and purge behavior for many low-value resources. Choose uniformity deliberately rather than treating completed Tasks as free wrappers.

When to create a Task

Create a Task when the remote agent owns work that the client may need to observe, pause, continue, cancel, or recover. A Task is not merely an asynchronous response. It is the protocol record for that unit of action.

The A2A 1.0 state model has two active states, two interrupted states, and four terminal outcomes, plus an unspecified fallback:

State Class Client interpretation
TASK_STATE_SUBMITTED Active The server accepted the work but may not have started it
TASK_STATE_WORKING Active Processing is underway
TASK_STATE_INPUT_REQUIRED Interrupted The server needs more information before it can continue
TASK_STATE_AUTH_REQUIRED Interrupted The work needs an authorization step defined outside the bare state value
TASK_STATE_COMPLETED Terminal Work finished successfully; inspect artifacts
TASK_STATE_FAILED Terminal The agent attempted the work and ended with an error
TASK_STATE_CANCELED Terminal Cancellation ended the work before completion
TASK_STATE_REJECTED Terminal The agent decided not to perform the work
TASK_STATE_UNSPECIFIED Indeterminate Treat as an unknown state, not as success

The difference between interrupted and terminal is operational. A client can send another Message to a task in INPUT_REQUIRED with the missing data. For AUTH_REQUIRED, the specification deliberately does not define a universal authorization grant. The agent implementation, credential issuer, or an A2A extension must define what is being authorized and how the client satisfies it. The state transition itself grants nothing.

Terminal Tasks are immutable. The server must not accept another message against a Task in COMPLETED, FAILED, CANCELED, or REJECTED. A refinement creates a new Task, usually in the same contextId, and may refer to the earlier task. This gives each output a stable input and lifecycle record.

Tasks are the stronger choice when any of these requirements applies:

  • the work may outlive the initiating connection;
  • the client needs progress, cancellation, polling, subscription, or push delivery;
  • the agent may pause for input or authorization;
  • several messages belong to one accepted unit of work;
  • output arrives as one or more artifacts, possibly in chunks;
  • operators need retention, listing, audit, or task-level access control;
  • a disconnect should not erase the client's handle to the work.

Creating a Task also creates responsibilities. The server needs clear state transition rules, ownership checks on GetTask and related operations, artifact retention, history limits, cancellation semantics, and a purge policy. The client needs to store the returned taskId, interpret interrupted states, and stop sending messages after a terminal state.

Design a hybrid agent

Many production agents should support both response types. A hybrid agent can answer capability questions directly, negotiate scope through Messages, then create a Task after it has enough information to accept work. This avoids premature task records while preserving a tracked lifecycle for substantive execution.

The decision should come from an explicit policy, not an unconstrained model choice. A model can classify intent, but application code should enforce task requirements for side effects, human approval, durable artifacts, delayed execution, and regulated workflows.

A server-side policy can evaluate the request in this order:

  1. Validate identity, message structure, supported content types, and protocol version. Protocol errors are not direct agent replies.
  2. Determine whether the Message continues an existing task. If it does, validate that the task is non-terminal and belongs to the same context.
  3. If the agent still needs to negotiate capability or scope before accepting work, return a direct Message in the current context.
  4. If execution requires a durable handle, interrupted states, artifacts, cancellation, or disconnected delivery, create a Task.
  5. Otherwise, complete the interaction with a direct Message.

One way to encode the boundary is a small policy function:

needs_task =
  has_side_effect
  or needs_human_input_after_acceptance
  or needs_separate_authorization
  or may_outlive_request
  or produces_managed_artifacts
  or requires_cancel_or_recovery
  or retention_policy_requires_task_record

This is an application rule, not text copied from the specification. Its value is predictability. The same operation returns the same lifecycle shape regardless of model phrasing, latency, or which internal tool happens to run.

Clients must still handle both variants advertised by the agent. Treating every SendMessage response as a Task will break on a valid direct Message. Treating it as a Message will discard the only handle to asynchronous work. Generate the response union from the official Protocol Buffer definition or use a conforming SDK, then test both branches.

The boundary should also remain distinct from the broader protocol choice. Trace Brief's A2A vs MCP comparison explains when a remote agent should own a Task at all. This page assumes A2A is already the interaction contract and decides how much lifecycle one exchange needs.

Delivery, waiting, and recovery

A Task does not force one delivery mode. A2A 1.0 supports blocking sends, immediate return plus polling, streaming, later subscription, and push notifications. The correct mode depends on connection lifetime and user experience.

Blocking send

With returnImmediately unset or false, a non-streaming SendMessage waits until the Task reaches a terminal or interrupted state. This works when completion is reasonably bounded and the client wants one response containing current status and artifacts. It still returns a Task, not a direct Message, because the server created the resource and lifecycle.

Immediate return and polling

With returnImmediately: true, the server returns the in-progress Task after creation. The client can call GetTask for current status, artifacts, and selected history. Polling is simple and works across network boundaries, but clients should use backoff and any server retry guidance rather than producing a constant request stream.

Streaming and subscription

SendStreamingMessage returns either exactly one direct Message and closes, or starts with a Task and follows it with status and artifact events. A client can also use SubscribeToTask for an existing non-terminal Task. The server's Agent Card must advertise streaming support.

The streaming and asynchronous operations guide describes Server-Sent Events for the HTTP binding. A task's lifecycle is independent of any one stream, so closing a browser tab or losing a socket should not cancel the work. After reconnection, the client can retrieve the Task or establish a new subscription.

Do not treat transient status messages as an event log. The normative specification warns that a reconnecting client may miss them. Persist durable progress in task state and durable results in artifacts. If exact event replay matters, the application needs an additional sequence or replay contract.

Push notifications

Push fits a client that cannot maintain a connection or poll continuously. The server posts updates to a registered webhook, while the client later calls GetTask for authoritative state. Push endpoints need their own authentication, URL validation, replay protection, rate limits, and network controls. A webhook is an external input surface, not a trusted callback merely because it carries a taskId.

Retries and duplicate work

The specification says Get operations are naturally idempotent and cancellation is idempotent. SendMessage may be idempotent, with messageId available for duplicate detection. That "may" is important. Clients should learn the server's retry contract before automatically resending a message that can create work or cause a side effect.

At minimum, retain the same messageId for the same logical message, record the returned taskId, and reconcile ambiguous timeouts with GetTask or an application idempotency key. A fresh messageId should mean a new communication turn, not another transport attempt.

Common lifecycle mistakes

Creating Tasks for every conversational turn

Always returning a completed Task makes the client branch simpler, but fills the task store with low-value records and blurs the difference between conversation and accepted work. Keep this model only if uniform auditing or client simplicity outweighs storage, listing, retention, and authorization costs.

Calling a direct Message "no state"

A direct Message can carry contextId, and the remote agent may use that context for later turns. The absence of a Task means there is no task lifecycle, not that the server has no conversational memory. Define context expiration separately from task retention.

Putting final output in a status Message

Messages explain or advance the interaction. Artifacts hold Task output. If a generated report, receipt, or structured result exists only in a status Message, retrieval after disconnection becomes unreliable and downstream clients cannot handle output consistently.

Sending more work to a terminal Task

A completed, failed, canceled, or rejected Task cannot restart. Create a new Task in the same context and use referenceTaskIds when the earlier work matters. This preserves the old result and gives the refinement its own terminal outcome.

Treating interrupted states as failure

INPUT_REQUIRED and AUTH_REQUIRED mean the Task is paused. A client that maps every non-working state to failure will abandon recoverable work. Present the requested input or authorization action, then continue the same non-terminal Task with a Message.

Treating AUTH_REQUIRED as permission

The state only communicates a need. It does not identify an approved operation, grant scope, or prove user consent. The agent must bind any credential or approval to a defined action and verify it before proceeding. The agent identity and attestation guide covers the wider problem of identities and evidence across agent boundaries.

Losing the identifier hierarchy

Generating task IDs on the client, mixing task and context IDs, or replacing messageId on every retry defeats protocol checks and duplicate detection. Store all three as separate fields in logs and traces. Avoid one generic conversation_id column that cannot say which lifecycle the event belongs to.

Trusting a successful state without validating artifacts

TASK_STATE_COMPLETED reports the remote agent's outcome. The client still needs to validate content type, schema, file size, malware risk, provenance, and any business invariant before using an artifact. Success is a lifecycle fact, not proof that the output is safe or correct.

Implementation checklist

Use this checklist at the server boundary:

  1. Write down what creates a durable unit of work in your product.
  2. Return a direct Message only when the interaction is complete and needs no task operations.
  3. Create a Task for delayed work, side effects, managed artifacts, later input, authorization, cancellation, recovery, or audit requirements.
  4. Generate taskId on the server and keep it distinct from contextId and messageId.
  5. Put durable Task results in Artifacts and reserve Messages for communication.
  6. Define valid state transitions, including how clients continue INPUT_REQUIRED and AUTH_REQUIRED work.
  7. Reject messages sent to terminal Tasks. Start refinements as new Tasks in the same context.
  8. Document task retention, context expiration, history limits, purge behavior, and access checks.

Then test the client against both legal SendMessage responses. Test a direct Message with a contextId; a Task returned in SUBMITTED; a fast Task returned already COMPLETED; both interrupted states; every terminal outcome; a mismatched task and context; a duplicate messageId; a stream disconnect; a missed status message; and an artifact that fails schema validation.

Use a Message to communicate. Create a Task when that communication commits the remote agent to managed work. Quick exchanges remain light, while durable work gets the identifiers, states, recovery paths, and artifacts its clients need. Trace Brief's editorial policy explains how this publication verifies changing protocol behavior and separates normative requirements from implementation advice.

Sources and methodology

This article draws on the primary documentation and research listed below. An editor reviewed the technical claims and wording before publication.

  1. Agent2Agent Protocol Specification 1.0.0 — normative operations, core objects, lifecycle states, identifiers, idempotency, update delivery, and artifact semantics
  2. Life of a Task — official guidance for message-only, task-generating, and hybrid agents, plus terminal task immutability
  3. Streaming and Asynchronous Operations — official streaming, push notification, and disconnected-client guidance
  4. What's New in A2A v1.0 — released v1.0 names for task states and current protocol changes
  5. A2A Protocol Buffer definition — authoritative service methods, response union, message fields, task fields, and event types