---
title: "Get an agent"
method: GET
path: "/v1/projects/{project_id}/agents/{agent_id}"
tags: ["Agents"]
---

# Get an agent

`GET /v1/projects/{project_id}/agents/{agent_id}`

## Request

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

Returns a single agent by ID. A credential scoped to a project the agent is shared with, through an accepted share, reads its `id` and `name` only.

## Path parameters

- `agent_id` string, required

## Response `200`

Agent details

- Agent
  - `id` string: Public ID of the agent
  - `project_id` string: Public ID of the owning project
  - `ai_provider_id` string, nullable: Public ID of the pinned AI provider. Null when the agent resolves its model through `model_route_id` instead.
  - `model_route_id` string, nullable: Public ID of the model route that resolves this agent's completion model. Null when the agent pins a provider through `ai_provider_id`. Mutually exclusive with `ai_provider_id` and `model`.
  - `name` string, nullable: Display name
  - `instructions` string, nullable: System instructions guiding behavior
  - `model` string, nullable: Model identifier
  - `tool_bindings` ToolBindingRead[], nullable: Tools attached to this agent, one binding object per tool — the canonical attachment field. See [Tool Bindings](/docs/modules/agents#tool-bindings).
    - `tool_id` string: Public ID of a persisted tool in the agent's own project.
    - `tool` object: Inline tool definition, in the `CreateToolRequest` shape. A literal credential is refused on write and a stored one is masked as `{"no_echo": true}`, exactly as on a persisted tool. A `{{secret:...}}` reference stays readable.
  - `max_steps` integer, nullable: Maximum agent loop steps before stopping. The budget bounds a **turn**: a generation that pauses at `requires_action` and resumes after `submit-tool-outputs` continues the same turn and spends what is left of it, so a turn that arrives with nothing left completes with `stop_reason: "max_steps"` instead of calling the model again.
  - `tool_choice` unknown
  - `stop_conditions` object[], nullable: Conditions that end the agent's work early, on top of `max_steps` — turn-scoped (`has_tool_call`) or chain-scoped (`max_chain_generations`). See the create request body for the accepted shapes.
  - `active_tool_ids` string[], nullable: Subset of the bound tools that are active
  - `guardrail_ids` string[], nullable: Guardrails attached at the agent scope, governing every tool call the agent makes.
  - `step_rules` object[], nullable: Per-step overrides of `tool_choice` and `active_tool_ids`. Steps are numbered from the first step of the **turn**, and that numbering spans a `requires_action` pause — a rule fires once per turn, not once per resumption.
  - `boundary_policy` object, nullable: Allowed/denied runtime actions
  - `temperature` number, nullable: Sampling temperature
  - `knowledge_config` object, nullable: Knowledge retrieval config injected before every generation
    - `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.
    - `write_memory_store_id` string, nullable: Public ID of the memory store the agent can write to during generation. When set, a write_memory tool is automatically available to the agent.
  - `output_schema` object, nullable: JSON Schema describing the structured object the model must return. When set, non-streaming generations constrain output to this schema and the parsed value is returned as `output.object`. The schema is enforced on the way back, not just sent to the model: an object that violates it fails the generation with 502 `OUTPUT_SCHEMA_VALIDATION_FAILED`, naming the violated field. Constraints beyond `required`/`type` (`minLength`, `enum`, `pattern`, `minItems`) are honored and are what reject a structurally valid but degenerate answer. See the Structured Output section in the Agents module docs.
  - `prompt_caching` object, nullable: Prompt caching for this agent's turns. `{"enabled": true}` marks two cache breakpoints: the end of the system block, which covers the tool definitions and the instructions, and the last message of each step, which covers the conversation so far. A provider that caches by explicit breakpoint then reads, on every later step and every later turn of the session, the whole prefix the previous one sent, and writes only what was appended. Null or omitted is off: a cache write costs more than an uncached token, so an agent whose prefix is never re-read would pay for the privilege. A prefix below the model's minimum cacheable length is not cached. Cache reads are reported as `cached_tokens` and cache writes as `cache_write_tokens` on usage; a write with no `cache_write_tokens` price bills at the input rate.
    - `enabled` boolean: Whether the breakpoint is marked. Defaults to false.
  - `max_context_messages` integer, nullable: Maximum number of recent messages to include in the context window sent to the model. When null, all messages are included.
  - `max_output_tokens` integer, nullable: Maximum number of tokens the model may generate per step of a turn. A response that reaches it stops with `stop_reason` `length`. Null leaves the model's default.
  - `reasoning` object, nullable: How much the model reasons before answering. Translated per provider (Anthropic thinking, Gemini thinking config, OpenAI reasoning effort) against what the model supports; a model that cannot reason ignores it. `none` turns reasoning off where the model allows it. Reasoning tokens are metered as output. Null leaves the model's default.
    - `effort` 'none' | 'low' | 'medium' | 'high', required
  - `single_session_per_actor` boolean: When true, only one open session per actor_id is allowed for this agent. Creating a second open session for the same actor returns 409.
  - `trace_content_mode` 'full' | 'none' | 'null', nullable: Agent-scope zero-retention setting. `null` (the default) inherits the project's `trace_content_mode`; `none` means this agent's trace and generation content is never persisted. An agent may tighten a storing project to `none` but cannot loosen a `none` project back to `full`.
  - `on_approval_expiry` 'terminate' | 'react' | 'null', nullable: What happens when one of this agent's held tool calls expires un-approved. `null` (the default) and `terminate` end the chain there — the expired approval, its `approvals.expired` event and the auto-filed `approval_expired` exception are the whole record. `react` spawns a continuation that reports the staleness to the agent, for an agent that acts on it.
  - `version` integer: Current config version. Starts at 1 and increments on every write that changes the config; each increment archives the new config as an `AgentVersion`. A write that changes nothing leaves it untouched.
  - `active_release` AgentRelease: A staged rollout splitting traffic between two archived versions. See [Versioning and Staged Rollout](/docs/modules/agents#versioning-and-staged-rollout).
    - `stable_version` integer, required: Version served to traffic not assigned to the canary
    - `canary_version` integer, required: Version under trial. Must differ from `stable_version`.
    - `canary_percent` integer, required: Percentage of traffic assigned to `canary_version`
    - `promotion_gate` string, nullable: Eval that must have a passing run against `canary_version` before `promote` is allowed, or null for an ungated rollout. The gate constrains only how the rollout ends — traffic is split the same way either way. See [Eval-gated promotion](/docs/modules/agents#eval-gated-promotion).
  - `created_at` string, date-time
  - `updated_at` string, date-time

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: 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)
