---
title: "List tasks"
method: GET
path: "/v1/projects/{project_id}/tasks"
tags: ["Tasks"]
---

# List tasks

`GET /v1/projects/{project_id}/tasks`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/tasks`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.naturali.ai/authorize`, token URL `https://api.naturali.ai/token`, scopes: `mcp:access`)

Lists tasks (the board query). Filter by workflow, state, status, automation status, assignee, or `metadata` — `GET /tasks?workflow_id=...&state=...` is one board column.

## Query parameters

- `workflow_id` string
- `state` string
- `status` string[]
- `automation_status` string[]
- `assignee` string
- `metadata` string
- `limit` integer
- `offset` integer

## Response `200`

A list of tasks

- object
  - `data` Task[], required
    - `id` string
    - `project_id` string
    - `workflow_id` string
    - `workflow_version` integer, nullable: The workflow version this task runs on, fixed when the task was created. Transitions, approval gates and payload validation all resolve through it, so editing the workflow never re-shapes a task already in flight. `null` for tasks created before pinning existed, which run on the live definition.
    - `title` string
    - `state` string
    - `status` 'open' | 'closed'
    - `payload` object: Caller-owned task data; input to guards (as `task.payload`) and dispatch mappings. The engine never writes into it except the workflow's declared `payload_writes`.
    - `metadata` MetadataBag: Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase. No key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.
    - `last_result` unknown
    - `assignee` string, nullable
    - `active_dispatch` object, nullable: { kind, id, status } of the current state's dispatch, if any. `kind` is `generation`, `orchestration_run` or `tool_call`; a `tool_call` always carries a null `id`, since a direct tool call leaves no addressable record. Carries an additional `attempt` (1-based) while the state's `on_enter.retry` policy is in effect.
    - `automation_status` 'running' | 'completed' | 'failed' | 'unrouted' | 'paused' | 'null', nullable: Status of the current state's dispatch. `null` until a state with an automation is entered. `paused` means an operator pause suppressed this state's `on_enter` before it ran, so resume-task will start it.
    - `pause_requested_at` string, date-time, nullable: When an operator paused this task's automation, or null when no pause is in force.
    - `pause_reason` string, nullable: The reason supplied with the pause, when one was.
    - `automation_chain_depth` integer: Server-owned. How many machine-driven transitions have run back-to-back with no outside intervention — a dispatch outcome routed through `on_complete`/`on_failure`, or a `transition-task` call made by a dispatched run or agent with its run-as token. Any move by a person, a plain API key, or an approval resolution resets it to `0`. Once it would exceed the server's limit (`TASK_AUTOMATION_CHAIN_LIMIT`, default 50) the next such transition is refused with `TASK_AUTOMATION_CHAIN_LIMIT`, bounding a cycle composed across workflows and orchestrations.
    - `pending_transition` string, nullable: The name of a `requires_approval` transition parked awaiting a human decision. Non-null while an ApprovalItem gates the move; the task stays in its current state and no other transition may fire until the approval resolves.
    - `entered_state_at` string, date-time
    - `created_at` string, date-time
    - `updated_at` string, date-time
  - `total` integer, required
  - `limit` integer, required
  - `offset` integer, required

## Other responses

- `401`: Unauthorized
- `403`: Forbidden

---

[API](https://skmtc.dev/naturali/apis/naturali-ai-api.md) · [All operations](https://skmtc.dev/naturali/apis/naturali-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/naturali/apis/naturali-ai-api/revisions/e4905b78f422?raw)
