---
title: "Get task history"
method: GET
path: "/v1/projects/{project_id}/tasks/{task_id}/history"
tags: ["Tasks"]
---

# Get task history

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

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/tasks/{task_id}/history`
- 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`)

Returns the append-only transition history of a task.

## Path parameters

- `task_id` string, required

## Query parameters

- `limit` integer
- `offset` integer

## Response `200`

The task's transition history, oldest first

- object
  - `data` TaskTransition[], required
    - `id` string
    - `task_id` string
    - `from_state` string, nullable
    - `to_state` string
    - `transition` string, nullable
    - `principal_type` 'user' | 'api_key' | 'agent' | 'automation' | 'approval' | 'system': What kind of principal acted. `user` and `api_key` are authenticated callers, and the id is the user's (`user_…`) or the key's own (`key_…`), so a record names which key acted rather than its owner. `agent` is an agent writing under its own identity (`agent_…`). `automation` is the task engine acting on a dispatch outcome and `approval` an approval resolution; neither carries an id. `system` is the platform itself, with an id naming the mechanism (`retention_sweep`, `zero_retention`).
    - `principal_id` string, nullable: Public id of the principal that made the move — the user (`user_...`), or for `api_key` auth the key's own id (`key_...`), distinguishing which key acted. Null for `automation`, which has no principal: the cause is carried by `generation_id` / `orchestration_run_id` / `tool_id`, one per dispatch kind — exactly one of which is set on an automation move.
    - `generation_id` string, nullable: Set when an `agent` dispatch's generation caused the move.
    - `orchestration_run_id` string, nullable: Set when an `orchestration` dispatch's run caused the move.
    - `tool_id` string, nullable: Set when a `tool` dispatch caused the move. A tool call produces no addressable record of its own, so the tool it called is what records why the task moved.
    - `note` string, nullable
    - `from_workflow_version` integer, nullable: Set on a migration row only: the version the task left, or null for a task that had no pinned version.
    - `to_workflow_version` integer, nullable: Set on a migration row only, the version the task moved onto.
    - `created_at` string, date-time
  - `total` integer, required
  - `limit` integer, required
  - `offset` integer, required

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Task not found

---

[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)
