> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corti.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Task lifecycle

> Learn how A2A tasks move through states, how streaming events deliver updates, and how to resume interrupted streams.

A task is a stateful unit of work performed by an agent. When you send a message to an agent, the response is either a direct `Message` (for quick operations) or a `Task` (for longer-running work). Tasks have a defined lifecycle, can produce artifacts, and can be streamed in real time.

## What is a task

A task represents work that an agent performs in response to a message. It has:

* A unique prefixed UUIDv7 identifier (`task.0192f4c8-...`)
* A `contextId` linking it to a [context](/agentic/context-memory) for conversation continuity
* A `status` with a current state and timestamp
* A `history` array of messages exchanged during the task
* An `artifacts` array of outputs produced by the task
* `metadata` including token and credit accounting under `$usage`

<Note>The agent decides whether to respond with a `Task` or a `Message`. Quick operations (short completions, classifications) often return a `Message` directly. Longer workflows return a `Task` that you monitor for completion.</Note>

## Task states

A task moves through the following states:

| State                       | Description                                                                                   |
| --------------------------- | --------------------------------------------------------------------------------------------- |
| `TASK_STATE_SUBMITTED`      | The task has been accepted by the agent but processing has not started                        |
| `TASK_STATE_WORKING`        | The agent is actively processing the task                                                     |
| `TASK_STATE_COMPLETED`      | The task finished successfully; artifacts are available                                       |
| `TASK_STATE_FAILED`         | The task failed; check the status message for error details                                   |
| `TASK_STATE_CANCELED`       | The task was canceled by the client                                                           |
| `TASK_STATE_INPUT_REQUIRED` | The agent needs additional input before it can continue                                       |
| `TASK_STATE_AUTH_REQUIRED`  | An MCP connector requires authentication; the task pauses until auth is provided              |
| `TASK_STATE_REJECTED`       | The task was rejected before processing (insufficient credits or invalid agent configuration) |

<Note>`TASK_STATE_REJECTED` and `TASK_STATE_AUTH_REQUIRED` are only visible on the JSON-RPC binding. On the REST (HTTP+JSON) binding, both are mapped to `TASK_STATE_FAILED` with a descriptive status message.</Note>

### Terminal vs non-terminal states

| Category                                                | States                                                                                                |
| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **Terminal** (immutable, cannot be canceled or resumed) | `TASK_STATE_COMPLETED`, `TASK_STATE_FAILED`, `TASK_STATE_CANCELED`, `TASK_STATE_REJECTED`             |
| **Non-terminal** (can be resumed or canceled)           | `TASK_STATE_SUBMITTED`, `TASK_STATE_WORKING`, `TASK_STATE_INPUT_REQUIRED`, `TASK_STATE_AUTH_REQUIRED` |

Canceling a task in a terminal state returns `409 Conflict`.

### State transitions

A task transitions through states as follows:

* **`TASK_STATE_SUBMITTED`** to **`TASK_STATE_WORKING`**: The agent begins processing.
* **`TASK_STATE_SUBMITTED`** to **`TASK_STATE_REJECTED`**: The task was rejected before processing (insufficient credits or invalid configuration).
* **`TASK_STATE_SUBMITTED`** to **`TASK_STATE_AUTH_REQUIRED`**: An MCP connector requires authentication before the agent can proceed.
* **`TASK_STATE_WORKING`** to **`TASK_STATE_COMPLETED`**: The task finishes successfully.
* **`TASK_STATE_WORKING`** to **`TASK_STATE_FAILED`**: An error occurred during processing.
* **`TASK_STATE_WORKING`** to **`TASK_STATE_INPUT_REQUIRED`**: The agent paused and needs user input.
* **`TASK_STATE_WORKING`** to **`TASK_STATE_AUTH_REQUIRED`**: An MCP connector encountered an auth error during execution.
* **`TASK_STATE_AUTH_REQUIRED`** to **`TASK_STATE_WORKING`**: Auth is resolved and the agent resumes.
* **`TASK_STATE_INPUT_REQUIRED`** to **`TASK_STATE_WORKING`**: The user provides input and the agent resumes.
* Any non-terminal state to **`TASK_STATE_CANCELED`**: The client cancels the task via `tasks/{id}:cancel`.

<Note>A task in `TASK_STATE_INPUT_REQUIRED` or `TASK_STATE_AUTH_REQUIRED` state is not terminated. Send a new message with the required input (for `TASK_STATE_INPUT_REQUIRED`) or resolve the connector authentication (for `TASK_STATE_AUTH_REQUIRED`) to resume processing. The SSE stream closes when either state is reached; you must send a new message or re-subscribe to continue receiving updates.</Note>

## Task history and messages

The `history` array contains all messages exchanged during the task, oldest first. Each message has:

* `messageId`: A prefixed UUIDv7 (e.g. `msg.0192f4c8-...`)
* `role`: Either `ROLE_USER` (sent by the client) or `ROLE_AGENT` (sent by the agent)
* `parts`: An ordered list of content parts (text, file, or data)
* `metadata`: Free-form metadata, including Corti's `$timestamp` for timing

You can control how much history the agent considers by setting `configuration.historyLength` on the [message send request](/agentic/guides/send-message).

## Artifacts

Artifacts are the tangible outputs a task produces. Each artifact has:

* `artifactId`: A prefixed UUIDv7 (e.g. `art.0192f4c8-...`)
* `name`: An optional human-readable name
* `parts`: Content parts containing the artifact data

Artifacts are closely tied to the task lifecycle. In healthcare scenarios, artifacts typically correspond to business outputs like clinical notes, coding suggestions, or extracted facts.

You can retrieve a specific artifact via:

```
GET /v2/agentic/contexts/{contextId}/tasks/{taskId}/artifacts/{artifactId}
```

## Streaming events

When you stream a message via `message:stream` or subscribe to an existing task via `tasks/{id}:subscribe`, the server sends Server-Sent Events (SSE). Each event carries an `A2AStreamResponse` with exactly one of the following fields:

| Field            | Description                                                                                                                                     |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `task`           | The full task object, sent when the task is first created or when its state changes                                                             |
| `message`        | A direct message response (no task lifecycle)                                                                                                   |
| `statusUpdate`   | An incremental status change with `taskId`, `contextId`, `status`, and `metadata` (no `final` flag; clients infer finality from the task state) |
| `artifactUpdate` | An incremental artifact update with `taskId`, `contextId`, `artifact`, and `lastChunk`                                                          |

### SSE event format

Each SSE frame follows the W3C SSE wire format:

| Field   | Description                                                                                        |
| ------- | -------------------------------------------------------------------------------------------------- |
| `data`  | The JSON-encoded payload (an `A2AStreamResponse`)                                                  |
| `event` | Event type (declared for forward compatibility; not currently sent by the server)                  |
| `id`    | Opaque event ID (the server writes event IDs but does not yet read `Last-Event-ID` for resumption) |
| `retry` | Reconnection interval (declared for forward compatibility; not currently sent by the server)       |

### Example SSE event

```json theme={null}
data: {"task":{"id":"task.0192f4c8-...","contextId":"ctx.0192f4c8-...","status":{"state":"TASK_STATE_WORKING","timestamp":"2026-05-19T12:00:00Z"}}}

data: {"artifactUpdate":{"taskId":"task.0192f4c8-...","contextId":"ctx.0192f4c8-...","artifact":{"artifactId":"art.0192f4c8-...","name":"icd10-result","parts":[{"text":"J45.909"}]},"lastChunk":true}}

data: {"statusUpdate":{"taskId":"task.0192f4c8-...","contextId":"ctx.0192f4c8-...","status":{"state":"TASK_STATE_COMPLETED","timestamp":"2026-05-19T12:00:01Z"}}}
```

## Resumption with Last-Event-ID

<Warning>The server writes event IDs but does not yet read the `Last-Event-ID` request header, so resumption without gaps is not implemented. The section below describes the intended design for when this feature ships.</Warning>

The intended resumption mechanism is to send the `Last-Event-ID` header with the most recent event ID you received. The server will replay events from that point forward once implemented.

This will work with both `message:stream` and `tasks/{id}:subscribe`. See [Stream responses](/agentic/guides/stream-responses) for implementation details.

## Task metadata and usage

Each task carries `metadata` with Corti's first-party keys prefixed with `$`. The `$usage` object provides token and credit accounting:

| Field                      | Description                                                       |
| -------------------------- | ----------------------------------------------------------------- |
| `model`                    | The model identifier that served the request                      |
| `inputTokens`              | Prompt tokens consumed                                            |
| `outputTokens`             | Completion tokens produced                                        |
| `cachedInputTokens`        | Subset of `inputTokens` served from the prompt cache (a discount) |
| `cacheCreationInputTokens` | Input tokens written to the prompt cache (cache-write surcharge)  |
| `totalTokens`              | Total tokens billed (`inputTokens` + `outputTokens`)              |
| `credits`                  | Corti billing credits charged for the task                        |

```json theme={null}
{
  "$usage": {
    "model": "corti-default",
    "inputTokens": 100,
    "outputTokens": 20,
    "cachedInputTokens": 64,
    "cacheCreationInputTokens": 0,
    "totalTokens": 120,
    "credits": 1.2
  }
}
```

You can use this data for cost tracking, budget monitoring, and evaluating cache effectiveness across your agents.

## Next steps

* Learn how to [send a message](/agentic/guides/send-message) and handle task or message responses
* Learn how to [stream responses](/agentic/guides/stream-responses) via SSE
* Read about [contexts](/agentic/context-memory) to understand how tasks are grouped
