Generations
One model loop each — the record every agent, session and conversation run is polled, audited and purged through.
Overview
A generation is created for you: running an agent, posting to a session, or generating into a conversation each produce one. What you get back immediately is its id, because generation is background by default — so this module is where a run's status, result, transcript and cost attribution are read from afterwards.
This module is a verbatim mirror of the runtime: every field, method, status code and error shape is the runtime's own, re-rooted under the project in the path.
See the OpenAPI spec for the full endpoint and schema reference, or browse it rendered under API Reference → Generations.
Data Model
Generation (selected fields)
The full field set is in the reference; these are the ones a caller usually acts on.
| Field | Type | Description |
|---|---|---|
id | string | Public generation ID — the polling handle a 202 hands back. |
agent_id | string | The agent that ran. |
status | string | in_progress, requires_action, completed, failed. |
stop_reason | string, nullable | Why it stopped: a model provider finish reason relayed unchanged (stop, tool-calls, length, …), or one the platform names itself — max_steps, depth_guard, chain_limit (see Continuation chains) — or error. |
error | object, nullable | Structured payload when status is failed; carries at least message. |
agent_version | integer | The agent config version that served this run — see Versioning and staged rollout. |
routing | object, nullable | What the model route did: which target answered, and what fell through. Absent for an agent with a pinned provider. |
metadata | object, nullable | Caller-owned annotations, set at create time or attached later. |
retrieval | array, nullable | What knowledge_config retrieval injected this turn, in order: a document chunk (document_id, document_version, chunk_id, page, similarity_score) or a memory (memory_store_id, memory_id, similarity_score). null when no retrieval ran, [] when it matched nothing. Kept by a content purge. |
initiator_generation_id | string, nullable | The generation that triggered this one, for nested agent calls. |
chain_id | string, nullable | The continuation chain this generation belongs to — set on the root and every continuation, null otherwise. |
content_redacted_at | string (date-time), nullable | Non-null once the content was purged. |
trace_id | string | The trace this generation belongs to. |
orchestration_run_id | string, nullable | The orchestration run that dispatched this generation, if any. |
node_id | string, nullable | The orchestration node that dispatched it. |
node_attempt | integer, nullable | The node's 1-based retry attempt — what tells two attempts of the same node apart. |
session_id | string, nullable | The session this generation was dispatched through, or null when it was started outside one. |
actor_id | string, nullable | The actor the generation was attributed to, or null when none was. |
Key Concepts
Background by default
A generate call answers 202 Accepted with a generation_id unless you pass
wait=true. Poll
GET /v1/projects/{project_id}/generations/{generation_id}
until status leaves in_progress:
completed— the result is on the generation, and on the conversation or session it ran in.requires_action— aclient-type tool is waiting on you; submit its output to resume the run.failed— readerror.message.
Your first agent generation
polls one to completed;
Run tools in your own code
parks one at requires_action and resumes it.
Passing wait=true holds the request open and returns the finished result
instead. Streaming (stream: true) implies waiting, and cannot be combined with
the background mode.
Reading a run back, step by step
GET /v1/projects/{project_id}/generations/{generation_id}/transcript
returns the turn as an ordered sequence of steps — what the model was asked, each
step's tool calls and their results, and how it ended. It is the answer to "why
did the agent do that", and it is the natural companion to a failed status.
Debug a failed run uses it to
name the tool call that broke a run whose status still reads completed.
Filtering the log
GET /v1/projects/{project_id}/generations
filters by agent, trace and status, so "every failed run of this agent" is one
call.
Roll out an agent version
filters by agent to see which version served each run.
It also filters by orchestration_run_id and node_id, which is how an
orchestration run is traced back to what its agent nodes
actually generated: a node execution record carries no generation id, so the
pointer lives on the generation. A node that was retried returns one generation
per node_attempt.
Filtering by chain_id is how a continuation chain is
expanded into its members — the chain record itself carries only their count.
session_id and actor_id narrow the same way: the turns behind one
conversation, or every run one end user started across every session they
appear in. Either naming nothing in the project yields an empty page rather
than the unfiltered log. A session's own cost is read directly off
GET /v1/projects/{project_id}/sessions/{session_id}
— these filters are for the runs behind that figure.
Continuation chains
A requires_action resumption, and a react approval expiry,
each start a new generation rather than mutating the one that paused — but both
stay part of the same continuation chain as the generation they continue.
Every member shares one chain_id; a generation outside any chain has it null.
Gate a tool with guardrails
finds the continuation an approval starts.
An agent's max_chain_generations stop condition
bounds a chain's length below the deployment's own ceiling. Reaching it ends the
chain with stop_reason: "chain_limit", and files a matching
chain_limit exception.
Purging content
DELETE /v1/projects/{project_id}/generations/{generation_id}/content
clears the run's content — metadata, error, extraction, and the internal
recovery state of a paused run — and stamps content_redacted_at together with
the principal that did it. The usage and cost records survive: what was spent
stays auditable after what was said is gone. To stop content being recorded in
the first place, set trace_content_mode on the
agent;
Run a zero-retention agent
shows the usage that survives.
Purging the trace instead — DELETE /v1/projects/{project_id}/traces/{trace_id}/content —
erases the steps object this generation's content lives in and cascades to every
generation under it, so that is the call to reach for when a whole run must go.
Response messages
output.response_messages carries the full message list the model produced —
tool calls, tool results, and the final text — as the shape the underlying SDK
returns it in.
One field is removed on the way out: providerOptions, the SDK's per-vendor
bag. Its keys name the vendor serving the request, which for a
naturali-managed provider would disclose infrastructure the
managed offering exists to abstract away. It is stripped for every provider, so
the shape does not differ between a managed model and your own credentials.
Nothing you can act on is lost: the field is not part of this API's contract,
and no message shape POST /v1/projects/{project_id}/agents/{agent_id}/generate
accepts could send it back.
trace_id is the trace this generation belongs to: fetch its
tree to see the sub-agent calls around it, and this generation's transcript to
see what it did.
Examples
Poll a background generation
- CLI
- SDK
- curl
naturali get-generation \
--project-id proj_V1StGXR8Z5jdHi6B \
--generation-id gen_V1StGXR8Z5jdHi6B
const { data: generation } = await naturali.generations.getGeneration({
path: {
project_id: 'proj_V1StGXR8Z5jdHi6B',
generation_id: 'gen_V1StGXR8Z5jdHi6B',
},
});
curl https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/generations/gen_V1StGXR8Z5jdHi6B \
-H "Authorization: Bearer $NATURALI_TOKEN"
List an agent's failed runs
- CLI
- SDK
- curl
naturali list-generations \
--project-id proj_V1StGXR8Z5jdHi6B \
--agent-id agent_V1StGXR8Z5jdHi6B \
--status failed
const { data: failed } = await naturali.generations.listGenerations({
path: { project_id: 'proj_V1StGXR8Z5jdHi6B' },
query: { agent_id: 'agent_V1StGXR8Z5jdHi6B', status: 'failed' },
});
curl "https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/generations?agent_id=agent_V1StGXR8Z5jdHi6B&status=failed" \
-H "Authorization: Bearer $NATURALI_TOKEN"
Read the transcript of one run
- CLI
- SDK
- curl
naturali get-generation-transcript \
--project-id proj_V1StGXR8Z5jdHi6B \
--generation-id gen_V1StGXR8Z5jdHi6B
const { data: transcript } = await naturali.generations.getGenerationTranscript(
{
path: {
project_id: 'proj_V1StGXR8Z5jdHi6B',
generation_id: 'gen_V1StGXR8Z5jdHi6B',
},
}
);
curl https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/generations/gen_V1StGXR8Z5jdHi6B/transcript \
-H "Authorization: Bearer $NATURALI_TOKEN"
Purge a run's content
- CLI
- SDK
- curl
naturali purge-generation-content \
--project-id proj_V1StGXR8Z5jdHi6B \
--generation-id gen_V1StGXR8Z5jdHi6B
await naturali.generations.purgeGenerationContent({
path: {
project_id: 'proj_V1StGXR8Z5jdHi6B',
generation_id: 'gen_V1StGXR8Z5jdHi6B',
},
});
curl -X DELETE https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/generations/gen_V1StGXR8Z5jdHi6B/content \
-H "Authorization: Bearer $NATURALI_TOKEN"