Agents API
An agent is a named, model-backed entity that carries a system prompt, a tool list, and optional compaction instructions. Agents are referenced by id when starting sessions or chats.
What an agent is, the turn loop, and how agents relate to chats and sessions.
Endpoints
| Method | Path | Summary |
|---|---|---|
| GET | /v1/agents |
List agents (offset or cursor pagination) |
| POST | /v1/agents |
Create an agent |
| GET | /v1/agents/{id} |
Get agent by id |
| PUT | /v1/agents/{id} |
Replace (full update) an agent |
| DELETE | /v1/agents/{id} |
Delete an agent |
| POST | /v1/agents/find |
Filter agents by predicate |
| GET | /v1/agents/{id}/status |
Validate the agent's external references |
Agent object
{
"id": "my-agent",
"description": "A coding assistant with file access.",
"model": {
"provider_id": "anthropic-prod",
"model_name": "claude-sonnet-4-6"
},
"system_prompt": ["You are a senior software engineer.", "Be concise."],
"compaction_prompt": ["Preserve open tasks and the current file under edit."],
"tools": ["system__list_files", "system__read_file"],
"temperature": null,
"max_output_tokens": null,
"harness_id": null
}
| Field | Required | Type | Description |
|---|---|---|---|
id |
no | string | Identifier (case-sensitive). If omitted, the server assigns a type-prefixed id (e.g. agent-3f9a1c8d). Immutable after creation |
description |
yes | string | Human-readable description |
model |
yes | AgentModel | LLM provider and model name (see below) |
system_prompt |
no | string[] | Multi-part system prompt; segments joined by the runtime. Empty list means no system prompt |
compaction_prompt |
no | string[] | Instructions for conversation compaction. Empty list falls back to the runtime default |
tools |
no | string[] | Scoped tool ids in the form <toolset_id>__<tool_name>. Empty list means no tools |
temperature |
no | number or null | Sampling temperature (>= 0.0). null defers to the adapter default |
max_output_tokens |
no | integer or null | Integer >= 1 or null. Hard cap on tokens generated per turn. null defers to the model default |
harness_id |
no | string or null | Set by harness management; mutation via CRUD returns 409 when set |
AgentModel fields:
| Field | Required | Description |
|---|---|---|
provider_id |
yes | Id of a configured LLMProvider |
model_name |
yes | Provider-side model name (e.g. claude-sonnet-4-6) |
Create an agent
POST /v1/agents - returns 201 Created.
curl -X POST https://your-host/v1/agents \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "my-agent",
"description": "A coding assistant with file access.",
"model": {"provider_id": "anthropic-prod", "model_name": "claude-sonnet-4-6"},
"system_prompt": ["You are a senior software engineer.", "Be concise."],
"tools": ["system__list_files", "system__read_file"],
"max_output_tokens": 2048
}'import httpx
r = httpx.post(
"https://your-host/v1/agents",
headers={"Authorization": f"Bearer {token}"},
json={
"id": "my-agent",
"description": "A coding assistant with file access.",
"model": {"provider_id": "anthropic-prod", "model_name": "claude-sonnet-4-6"},
"system_prompt": ["You are a senior software engineer.", "Be concise."],
"tools": ["system__list_files", "system__read_file"],
"max_output_tokens": 2048,
},
)
assert r.status_code == 201const r = await fetch("/v1/agents", {
method: "POST",
headers: {"Authorization": `Bearer ${token}`, "Content-Type": "application/json"},
body: JSON.stringify({
id: "my-agent",
description: "A coding assistant with file access.",
model: {provider_id: "anthropic-prod", model_name: "claude-sonnet-4-6"},
system_prompt: ["You are a senior software engineer.", "Be concise."],
tools: ["system__list_files", "system__read_file"],
max_output_tokens: 2048
})
})Response 201 Created - the full agent object as shown above.
Errors:
422- validation failed (missing required field, emptyid, etc.)409- an agent with thisidalready exists
Get an agent
GET /v1/agents/{id} - returns 200 OK with the agent object.
curl https://your-host/v1/agents/my-agent \
-H "Authorization: Bearer $TOKEN"import httpx
r = httpx.get("https://your-host/v1/agents/my-agent",
headers={"Authorization": f"Bearer {token}"})const r = await fetch("/v1/agents/my-agent", {
headers: {"Authorization": `Bearer ${token}`}
})Errors: 404 if the id does not exist.
List agents
GET /v1/agents - returns an offset or cursor page of agent objects.
Query parameters: limit (1-200, default 20), offset (default 0), cursor, order_by.
curl "https://your-host/v1/agents?limit=50&offset=0" \
-H "Authorization: Bearer $TOKEN"import httpx
r = httpx.get("https://your-host/v1/agents",
headers={"Authorization": f"Bearer {token}"},
params={"limit": 50, "offset": 0})
page = r.json()
agents = page["items"]const r = await fetch("/v1/agents?limit=50&offset=0", {
headers: {"Authorization": `Bearer ${token}`}
})
const {items, total, length, offset} = await r.json()Replace an agent
PUT /v1/agents/{id} - full replacement; returns 200 OK with the updated agent.
The body uses the same schema as POST. All fields are replaced; omitted optional fields reset to their defaults.
curl -X PUT https://your-host/v1/agents/my-agent \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "my-agent",
"description": "Updated description.",
"model": {"provider_id": "anthropic-prod", "model_name": "claude-sonnet-4-6"},
"system_prompt": ["Updated system prompt."],
"tools": []
}'import httpx
r = httpx.put(
"https://your-host/v1/agents/my-agent",
headers={"Authorization": f"Bearer {token}"},
json={
"id": "my-agent",
"description": "Updated description.",
"model": {"provider_id": "anthropic-prod", "model_name": "claude-sonnet-4-6"},
"system_prompt": ["Updated system prompt."],
"tools": [],
},
)await fetch("/v1/agents/my-agent", {
method: "PUT",
headers: {"Authorization": `Bearer ${token}`, "Content-Type": "application/json"},
body: JSON.stringify({
id: "my-agent",
description: "Updated description.",
model: {provider_id: "anthropic-prod", model_name: "claude-sonnet-4-6"},
system_prompt: ["Updated system prompt."],
tools: []
})
})Errors: 404 if not found, 409 if the agent is managed by a harness.
Delete an agent
DELETE /v1/agents/{id} - returns 204 No Content. Idempotent: deleting a non-existent id returns 404.
curl -X DELETE https://your-host/v1/agents/my-agent \
-H "Authorization: Bearer $TOKEN"import httpx
r = httpx.delete("https://your-host/v1/agents/my-agent",
headers={"Authorization": f"Bearer {token}"})
assert r.status_code == 204await fetch("/v1/agents/my-agent", {
method: "DELETE",
headers: {"Authorization": `Bearer ${token}`}
})Find agents by predicate
POST /v1/agents/find - filter, sort, and paginate agents server-side. Returns 200 OK with an offset or cursor page.
curl -X POST https://your-host/v1/agents/find \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"predicate": {
"kind": "predicate",
"op": "~=",
"left": {"kind": "field", "name": "id"},
"right": {"kind": "value", "value": "coding-%"}
},
"page": {"kind": "offset", "offset": 0, "length": 20}
}'import httpx
r = httpx.post(
"https://your-host/v1/agents/find",
headers={"Authorization": f"Bearer {token}"},
json={
"predicate": {
"kind": "predicate", "op": "~=",
"left": {"kind": "field", "name": "id"},
"right": {"kind": "value", "value": "coding-%"},
},
"page": {"kind": "offset", "offset": 0, "length": 20},
},
)await fetch("/v1/agents/find", {
method: "POST",
headers: {"Authorization": `Bearer ${token}`, "Content-Type": "application/json"},
body: JSON.stringify({
predicate: {
kind: "predicate", op: "~=",
left: {kind: "field", name: "id"},
right: {kind: "value", value: "coding-%"}
},
page: {kind: "offset", offset: 0, length: 20}
})
})Validate agent status
GET /v1/agents/{id}/status - checks whether the agent's model.provider_id (an LLMProvider row) and any non-built-in toolsets referenced by tools exist in storage. Returns 200 OK with {"ok": boolean, "issues": [string, ...]}. It does not call the live LLM or toolset providers.
curl https://your-host/v1/agents/my-agent/status \
-H "Authorization: Bearer $TOKEN"import httpx
r = httpx.get("https://your-host/v1/agents/my-agent/status",
headers={"Authorization": f"Bearer {token}"})const r = await fetch("/v1/agents/my-agent/status", {
headers: {"Authorization": `Bearer ${token}`}
})Errors: 404 if the agent id does not exist.
Errors note
All error responses use the RFC 7807 ProblemDetails envelope with type, title, status, detail, instance, and extensions (which includes request_id and, for 422 errors, an errors array with field paths). See the REST API overview for details.