---
title: "Run an agent generation"
method: POST
path: "/v1/projects/{project_id}/agents/{agent_id}/generate"
tags: ["Agents"]
---

# Run an agent generation

`POST /v1/projects/{project_id}/agents/{agent_id}/generate`

## Request

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

Sends messages to the agent, resolves its tools, and runs the AI model loop. Background by default: returns `202 Accepted` with a `generation_id` to poll via `GET /v1/projects/{project_id}/generations/{generation_id}`. Pass `?wait=true` to block and receive the result inline, where client tools pause the generation and return `requires_action`. Streaming (`stream: true`) implies waiting. Pass `idempotency_key` to make a retry safe: a request whose key is already claimed runs nothing and answers `202` with the generation the key names.

A credential scoped to a project the agent is shared with, through an accepted share, runs it in that project: on the publisher's configuration, recorded, metered and governed in the grantee project.

## Path parameters

- `agent_id` string, required

## Query parameters

- `wait` boolean

## Headers

- `Idempotency-Key` string

## Request body

- CreateAgentGenerationRequest
  - `messages` object[], required: Conversation turns, oldest first. Roles are `user` and `assistant`: the system prompt is the agent's `instructions`, so a `system` entry is refused with `400 SYSTEM_MESSAGE_NOT_ALLOWED`.
    - `role` 'user' | 'assistant', required
    - `content` union, required
      - string
      - ToolOutputMessageContent
        - `type` 'tool_output', required
        - `tool_id` string, required: Public ID of the tool to execute before generation.
        - `action` string, nullable: Optional action name for tools that require action selection (for example `mcp` tools).
        - `input` object, nullable: Input payload passed to the tool call.
        - `output_path` string, nullable: Optional dot-notation path used to extract a value from the tool output.
      - DocumentMessageContent
        - `type` 'document', required
        - `document_id` string, required: Public ID of a document to use as the message content.
  - `stream` boolean: When true the response is an SSE stream
  - `trace_id` string: Optional trace ID to group generations. Each generation appends its own steps to the trace's steps object, and `step_count` covers them all.
  - `parent_trace_id` string, nullable: The trace ID of the parent agent generation that triggered this one (for agent-to-agent calls)
  - `root_trace_id` string, nullable: The trace ID of the root generation in the call chain; if omitted, this generation is the root
  - `max_call_depth` integer: Maximum nested agent-call depth; 0 short-circuits with a depth-guard response
  - `tool_context` object, nullable: Key-value pairs forwarded as `X-Naturali-Context-<key>` headers on every `http` and `mcp` tool call in this generation. The header name is the deployment's configured context prefix (`X-Naturali-Context-` by default) plus the key verbatim — no character is re-cased. Keys are never case-converted — they round-trip exactly as sent. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY`.
  - `action_id` string: Logical action label recorded on the generation's usage meter, so spend can be rolled up per action (e.g. an A/B/C/D operating action).
  - `guardrail_context` object, nullable: Caller-supplied guardrail context (the `context.*` namespace guard and class expressions read at tool-dispatch time). Free-form and never interpreted by the platform; a guardrail may combine it with a `context_tool` per its `context_mode`. See the guardrails module.
  - `metadata` NullableMetadataBag, nullable: A `MetadataBag` on a request field where `null` is meaningful: a full-replacement update that clears the bag. A record whose bag is unset leaves the field out.
  - `idempotency_key` string: Deduplication key, unique within the project, that makes a retry of an at-least-once delivery or an ambiguous failure safe. The first request under a key runs the generation; any later request carrying it runs nothing and answers `202` with the generation the key names, whatever state it has reached — including a retry that arrives while the original is still running. The key is claimed by the generation record for as long as the record exists. Every other body field except `stream` is the request the key names: reusing a key with any of them changed, or on another agent of the project, is `409 IDEMPOTENCY_KEY_REUSED`. `stream` and `wait` say how the caller receives the generation, not what it is, so a retry may flip them.
  - `knowledge_config` object, nullable: Per-generation knowledge retrieval override. Array filters (memory_store_ids, document_ids, document_paths) are unioned with the agent's stored knowledge_config; `tags` pairs are merged with the override winning per key; scalar fields (min_score, rrf_k, recency_half_life_days, limit) use the per-generation value when present.
    - `memory_store_ids` string[]
    - `document_ids` string[]
    - `document_paths` string[]
    - `tags` TagBag: Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search. Keys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag. The bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.
    - `min_score` number: Minimum raw cosine similarity (0–1) a vector candidate must reach to be ranked, the floor the search endpoint spells `min_similarity`. No default: omitted means no floor at all, and every one of the `limit` nearest chunks is injected however weak it is.
    - `rrf_k` integer: The `k` in the fusion term `1 / (k + rank)`, the same knob knowledge search takes. Smaller weights the top of each ranking more heavily. Omitted, the deployment's `KNOWLEDGE_RRF_K` applies.
    - `recency_half_life_days` number: Half-life in days of the decay applied to **memory** results after fusion, the same knob knowledge search takes. `0` disables it. Omitted, the deployment's `KNOWLEDGE_RECENCY_HALF_LIFE_DAYS` applies.
    - `limit` integer: Maximum number of results to inject. Omitted, 10 are injected.

## Response `200`

Generation result or SSE stream (only when `?wait=true` or `stream: true`)

- AgentGenerationResponse: Result of an agent generation. Mirrors the server's `GenerationResult`. When `status` is `completed` the model output is under `output`; when it is `requires_action` the pending client tool calls are under `required_action`.
  - `id` string, required: Public ID of the generation
  - `trace_id` string, required: Public ID of the trace for this generation
  - `status` 'completed' | 'requires_action', required: Generation status
  - `ai_provider_id` string, nullable: Public ID of the AI provider that served `output.model` — the target a model route picked, or the agent's pinned provider. A model string alone does not identify its provider: two providers in one project can serve byte-identical model names, so this is what makes the value safe to map back to a name a gateway in front of this runtime publishes. Null when the generation resolved no serving provider.
  - `output` object, nullable: Model output (present when `status` is `completed`).
    - `model` string, required: Model that produced the output
    - `content` string, required: Final text output
    - `finish_reason` string, required: Reason the model stopped generating
    - `response_messages` object[], nullable: Full AI SDK response messages (tool calls, tool results, final text)
    - `object` object, nullable: Structured object matching the agent's `output_schema` (when `output_schema` is set)
  - `required_action` object, nullable: Pending action the caller must satisfy (present when `status` is `requires_action`).
    - `type` 'submit_tool_outputs', required: The kind of action required
    - `tool_calls` object[], required: Pending tool calls to execute and submit outputs for
      - `id` string: Tool call ID
      - `tool_name` string: Name of the tool to invoke
      - `args` object: Arguments for the tool call

## Other responses

- `202`: Generation accepted and running in the background (default, when `wait` is omitted or `false`), or — in every mode, streamed included — a duplicate request: the generation the `idempotency_key` already names is returned in whatever state it has reached and nothing runs. Poll `GET /v1/projects/{project_id}/generations/{generation_id}` for the result.
- `400`: Bad Request (e.g. an `idempotency_key` that is not a non-empty string of at most 255 characters)
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Agent or AI provider not found
- `409`: `IDEMPOTENCY_KEY_REUSED` — the key is already claimed by a generation started from a different request. Nothing runs.
- `429`: `QUOTA_EXCEEDED`: an `enforce`-mode generation quota is exhausted; `error.meta` carries `quota_id`, `metric`, `limit`, `window` and `resets_at`. `SHARE_CAP_EXCEEDED`: the agent is another project's, reached through a share whose `cap` is spent for the window; `error.meta.retry_after` says when. Both carry a `Retry-After` header.
- `502`: Upstream AI provider error (AI_PROVIDER_ERROR); model output that does not satisfy the agent's `output_schema` (OUTPUT_SCHEMA_VALIDATION_FAILED — the violated field is named in the message); or a model that wrote a tool invocation out as plain assistant text instead of calling the tool, so the tool never ran (TEXT_ENCODED_TOOL_CALL — `meta.tool_name` names the tool). The error `meta` includes the `generation_id` and `trace_id` of the failed generation for post-mortem debugging via GET /v1/projects/{project_id}/generations/{generation_id}. Streaming requests report the provider error in a terminal SSE frame instead, since their status line is already on the wire.

---

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