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

# List generations

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

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/generations`
- 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 generations the caller can access, optionally filtered by agent, trace, orchestration run, node, and status. The generations of one trace are the `trace_id` filter.

Filtering by `orchestration_run_id` is the supported way to get from an orchestration run to the generations its agent nodes produced: a node execution record carries no generation id, so the pointer lives here, alongside the run's other attribution columns.

## Query parameters

- `agent_id` string
- `trace_id` string
- `session_id` string
- `actor_id` string
- `initiator_generation_id` string
- `chain_id` string
- `orchestration_run_id` string
- `node_id` string
- `status` string[]
- `limit` integer
- `offset` integer

## Response `200`

Paginated list of generations

- object
  - `data` Generation[]
    - `id` string: Public ID of the generation
    - `project_id` string: Public ID of the project
    - `agent_id` string: Public ID of the agent that ran this generation
    - `trace_id` string: Public ID of the trace this generation belongs to
    - `initiator_generation_id` string, nullable: Public ID of the generation that started this one: a sub-agent invocation, an approval continuation, a client-tool re-handoff or a memory-rule handler turn. Null for top-level generations.
    - `chain_id` string, nullable: Public ID of the continuation chain this generation belongs to. Set on every member of a chain — the continuations and the root they descend from — and null on a generation that is not part of one.
    - `conversation_id` string, nullable: Public ID of the conversation this turn served, or null for a generation started outside one — a direct call, a trigger, an orchestration node. This is the edge a memory assertion walks up to reach the conversation a fact was learned in.
    - `session_id` string, nullable: Public ID of the session this generation was dispatched through, or null for a generation started outside one. This is the link a per-session cost reading follows; the session's own `usage` field reports the roll-up directly.
    - `actor_id` string, nullable: Public ID of the end-user actor the generation was attributed to, or null when none was. Copied onto the generation's usage event.
    - `started_by_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`).
    - `started_by_principal_id` string, nullable: Public id of that principal: the user (`user_…`), or for API key auth the key itself (`key_…`), so the record names which key acted.
    - `status` 'running' | 'requires_action' | 'completed' | 'failed': Lifecycle status of the generation
    - `started_at` string, date-time
    - `completed_at` string, date-time, nullable: When the generation reached a terminal state
    - `last_activity_at` string, date-time, nullable
    - `stop_reason` string, nullable: Why the generation stopped. Either the model provider's own finish reason relayed unchanged ('stop', 'tool-calls', 'length', …) or one the platform names itself: 'max_steps' when the turn spent its whole step budget on tool calls, 'depth_guard' when a nested call exceeded the call depth, 'chain_limit' when a continuation chain reached its generation budget, or 'error' when the turn failed.
    - `error` StoredError: Why a resource's work failed, in the shape of an error response's `error` without `hint` and `docs_url`. Every `error` field a resource records carries it, so `code` can be read without first checking the type of `error`.
      - `code` string, required: A key from the server's ERROR_CODES registry, documented on the Error Codes page. `INTERNAL_ERROR`, with the message `Internal Server Error`, records an unexpected failure whose detail reaches the server log only.
      - `message` string, required
      - `meta` object: The failure's own structured context, when it names any.
    - `action_id` string, nullable: Logical action label supplied on the generate request. Recorded on the generation's usage event for per-action spend rollups.
    - `trigger_id` string, nullable: Trigger that initiated the generation, when applicable
    - `orchestration_run_id` string, nullable: Orchestration run that dispatched the generation. Null for a standalone generation.
    - `node_id` string, nullable: Node within `orchestration_run_id` that dispatched the generation. Together with the run it forms the usage event's replay identity.
    - `node_attempt` integer, nullable: The node's 1-based retry attempt, completing the run + node + attempt replay identity. A retried node produces one generation per attempt; this is what tells them apart. Null for a generation no orchestration node dispatched.
    - `agent_version` integer, nullable: Agent config version that served this generation, resolved by the served-version resolver (see [agent versions](/docs/modules/agents#versions-and-releases)).
    - `extraction` object, nullable: What each [memory rule](/docs/modules/memories#memory-rules) bound to `generations.completed` wrote for this turn, keyed by the rule's id — a store may have several rules, and one flat pair of counts could not say which produced them. Absent when no rule fired. Rules bound to `conversation_messages.generated` are recorded only as memory assertions. The rows behind every count are listed by `GET /v1/projects/{project_id}/generations/{generation_id}/memory-assertions`, so the summary and what it summarizes can be reconciled.
    - `tool_surface` object, nullable: What this turn's tool definitions cost to send — the one part of a prompt no provider reports and no caller can derive, since the tool block is a constant inside the reported totals and identical on every step. Null when the surface was never measured: a generation from before the field, or a path that resolves none. That is **not** what `tools: 0` means, which is a measured agent with nothing bound. Not a meter. `estimated_tokens` is an estimate and is never priced — `usage` and the usage events are the billing record.
      - `tools` integer: Tools resolved for the turn, before any per-step narrowing by `step_rules`.
      - `bytes` integer: Serialized length of those definitions — name, description and input schema — in canonical JSON, not the provider's wire format. Exact, and comparable across providers, which is what makes two agents' surfaces worth comparing.
      - `estimated_tokens` integer: `bytes` over a measured bytes-per-token ratio. An estimate, and named one.
    - `retrieval` union[], nullable: What `knowledge_config` retrieval injected into this turn, in the order it was injected: which document, at which version, and which chunk, or which memory. Written by the server at turn start and never writable; pointers only, never the text. Null when no retrieval ran — the agent has no `knowledge_config`, or the turn had neither a query nor a filter to search with. An empty array is a retrieval that ran and matched nothing. Not content: a content purge leaves it standing, and zero retention still writes it. `document_version` names an archived version, so the text the turn read stays readable after the document changes.
      - union
        - GenerationRetrievedDocument
          - `source_type` 'document', required
          - `document_id` string, required: Public ID of the document the chunk belongs to
          - `document_version` integer, nullable, required: The document version the chunk was read from. Read it back with the document's versions to see the exact text the turn was given.
          - `chunk_id` string, required: Public ID of the injected chunk. Stops resolving once the document is re-chunked; `document_version` is the durable pointer.
          - `page` integer, nullable, required: Page within the source PDF (1-indexed). Null for plain text.
          - `similarity_score` number, nullable, required: Raw cosine similarity (0–1) between the turn's query and the result. Null when the turn had no query, or the search answered from the lexical channel alone. The rank is the array order.
        - GenerationRetrievedMemory
          - `source_type` 'memory', required
          - `memory_store_id` string, required: Public ID of the memory store the memory belongs to
          - `memory_id` string, required: Public ID of the injected memory
          - `similarity_score` number, nullable, required: Raw cosine similarity (0–1) between the turn's query and the result. Null when the turn had no query, or the search answered from the lexical channel alone. The rank is the array order.
    - `idempotency_key` string, nullable: The deduplication key the generation was started under, unique within the project and claimed for as long as the generation record exists. Null for a generation started without one.
    - `usage` UsageTotals: Token counts and cost for one slice of the meter. Tokens and cost only: a `compute_second` or `gb_day` meter is reported by the aggregate's `components` array. The three input dimensions partition the prompt — `input_tokens` = `uncached_input_tokens` + `cached_tokens` + `cache_write_tokens` — and are separate because they are separately priced. Note that the `input_tokens` **component** on a usage event is the uncached figure alone; here `input_tokens` is the whole prompt.
      - `cost_usd` number, nullable: Sum of priced component costs; null when nothing in the slice was priced. Always null at step altitude — the ledger prices one event per generation at write time, and a price read back per step would disagree with it after any price change.
      - `input_tokens` integer: Full prompt tokens, reconstructed from the components.
      - `uncached_input_tokens` integer: Prompt tokens priced at the plain input rate — the prompt minus cache reads and cache writes.
      - `output_tokens` integer
      - `cached_tokens` integer: Prompt tokens served from the provider's prompt cache.
      - `cache_write_tokens` integer: Prompt tokens written into the provider's prompt cache.
      - `reasoning_tokens` integer: A non-billable subset of `output_tokens`, reported where the provider breaks it out.
    - `routing` object, nullable: What the model route did for this generation. Present only when the agent resolves its model through a `model_route_id`.
      - `route_id` string: The route that resolved the model
      - `target_index` integer, nullable: Position in the route's `targets` of the target that served the last LLM call; null when every attempt failed.
      - `fallbacks` integer: How many times the route moved past a target during this generation (cumulative across a multi-step run).
      - `attempts` object[]: Every attempt, in order, across every LLM call of the run. An attempt with no `error_class` succeeded.
        - `target_index` integer
        - `ai_provider_id` string
        - `model` string
        - `error_class` 'provider_error' | 'timeout' | 'rate_limited': Why the attempt failed. Absent on the serving attempt, and absent on a deterministic failure (which fails the generation instead of failing over).
    - `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.
    - `content_redacted_at` string, date-time, nullable: When the generation's content was purged. Non-null means `metadata`, `error`, `extraction` and the internal recovery state have been cleared, while the usage/audit skeleton — ids, timestamps, status, stop reason and the attribution fields — is preserved.
    - `content_redacted_by_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`).
    - `content_redacted_by_principal_id` string, nullable: Public id of that principal — the API key's own id for key auth, so the record names which key acted. `retention_sweep` or `zero_retention` for `system`.
    - `created_at` string, date-time
    - `updated_at` string, date-time
  - `total` integer
  - `limit` integer
  - `offset` integer

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