Generations

Update generation metadata

Attaches caller-supplied key/value metadata to a generation record for per-run audit attribution (e.g. the ticket or case an AI action belongs to). The provided keys are shallow-merged over the existing metadata, so repeated patches accumulate. The bag is caller-owned and no key is reserved: server-owned state (usage attribution, the served agent version, the route's record, the extraction summary, what knowledge retrieval served) lives in its own top-level fields and cannot be written from here.

patch/v1/projects/{project_id}/generations/{generation_id}

Request

  • Base URL: https://api.naturali.ai
  • URL: https://api.naturali.ai/v1/projects/{project_id}/generations/{generation_id}
  • 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)

Path parameters

generation_idstring required

Public ID of the generation

Request body

metadataMetadataBag required

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.

Example request

{
  "metadata": {
    "author": "John",
    "revision": 2
  }
}

Response

Updated generation

idstring

Public ID of the generation

project_idstring

Public ID of the project

agent_idstring

Public ID of the agent that ran this generation

trace_idstring

Public ID of the trace this generation belongs to

initiator_generation_idstring 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_idstring 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_idstring 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_idstring 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_idstring 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_idstring 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_atstring date-time
completed_atstring date-time nullable

When the generation reached a terminal state

last_activity_atstring date-time nullable
stop_reasonstring 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.

action_idstring nullable

Logical action label supplied on the generate request. Recorded on the generation's usage event for per-action spend rollups.

trigger_idstring nullable

Trigger that initiated the generation, when applicable

orchestration_run_idstring nullable

Orchestration run that dispatched the generation. Null for a standalone generation.

node_idstring nullable

Node within orchestration_run_id that dispatched the generation. Together with the run it forms the usage event's replay identity.

node_attemptinteger 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_versioninteger nullable

Agent config version that served this generation, resolved by the served-version resolver (see agent versions).

extractionobject nullable

What each memory rule 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.

idempotency_keystring 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.

metadataMetadataBag

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_atstring 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_idstring 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_atstring date-time
updated_atstring date-time

Example response

{
  "id": "gen_V1StGXR8Z5jdHi6B",
  "started_by_principal_type": "user",
  "started_by_principal_id": "user_V1StGXR8Z5jdHi6B",
  "status": "failed",
  "stop_reason": "error",
  "error": {
    "code": "AI_PROVIDER_ERROR",
    "message": "Provider returned 402: insufficient credits"
  },
  "tool_surface": {
    "tools": 61,
    "bytes": 152161,
    "estimated_tokens": 45148
  },
  "retrieval": [
    {
      "source_type": "document",
      "document_id": "doc_V1StGXR8Z5jdHi6B",
      "document_version": 3,
      "chunk_id": "dchunk_V1StGXR8Z5jdHi6B",
      "page": 2,
      "similarity_score": 0.82
    }
  ],
  "idempotency_key": "discord-1287654321098765432",
  "metadata": {
    "author": "John",
    "revision": 2
  },
  "content_redacted_by_principal_type": "user",
  "content_redacted_by_principal_id": "user_V1StGXR8Z5jdHi6B"
}

Changes

No recorded changes to this operation across all 1 revision of this API.