Create an agent conversation
Creates a new agent conversation with a pinned system_prompt and a defaults block
that every subsequent run inherits. The conversation starts at version: 0 — your
first run’s expected_version will be 0.
What this endpoint actually does
- Validates the
defaultsblock. Failures here (alias regex, duplicate aliases, wire-name length, missing required fields) return 400 immediately — the conversation row is not created on validation failure. - Inserts a row in
agent_conversationswithversion = 0,company_idfrom your bearer token, and the validateddefaultsserialized as JSONB. - Returns the persisted conversation echo.
No workflows are started here. The conversation is a long-lived container; the
actual model execution happens later, in POST /agents/conversations/{id}/runs.
What you cannot change later
system_prompt— pinned for the conversation’s lifetime. If you need a different system prompt, create a new conversation.company_id/user_id— derived from the bearer token at creation time; not exposed in the request body. Both gate every subsequent read endpoint on this conversation: peers in the same company cannot see each other’s conversations, runs, or messages. Cross-user access surfaces as 404 — the API does not distinguish “doesn’t exist” from “owned by another user”.
Everything else in defaults (model, max_iterations, tools, etc.) is just a default
— any individual run can override any of those fields via config_override on its
POST .../runs body. See Agent Conversations
Reference
for the override semantics.
Permission
agent_conversations resource with write verb. The same permission gates
POST .../runs; read is required for the GET endpoints.
Example: minimal “hello world” conversation (no tools)
{
"name": "hello world",
"system_prompt": "You are a helpful assistant. Answer concisely.",
"defaults": {
"model": "anthropic.claude-opus-4.6",
"data_plane_id": "f79cbdae-4848-47ca-95e8-69588364d185",
"execution_cluster": "shared",
"max_iterations": 3,
"max_tokens": 1024,
"temperature": 0.0,
"output_format_schema": {
"type": "object",
"additionalProperties": false,
"required": ["text"],
"properties": { "text": { "type": "string" } }
}
}
}
Example: conversation that can search the Narrative docs
The mcp_servers entry registers two tools the model can call server-side. The
platform handles the JSON-RPC calls to https://docs.narrative.io/mcp transparently.
{
"name": "docs search",
"system_prompt": "Use the available tools to look things up before answering.",
"defaults": {
"model": "anthropic.claude-opus-4.6",
"data_plane_id": "f79cbdae-4848-47ca-95e8-69588364d185",
"execution_cluster": "shared",
"max_iterations": 8,
"max_tokens": 2048,
"temperature": 0.0,
"output_format_schema": {
"type": "object",
"additionalProperties": false,
"required": ["text"],
"properties": { "text": { "type": "string" } }
},
"mcp_servers": [{
"alias": "docs",
"url": "https://docs.narrative.io/mcp",
"description": "Narrative docs MCP server"
}]
}
}
Example: conversation with a caller-declared tool (human in the loop)
Caller-declared tools[] entries cause the model’s call to pause the run with
status: requires_action. The caller answers by posting a follow-up run with
payload.kind: tool_outputs. These tools have no alias — the model sees the bare
name, and the platform routes any dash-free tool call to caller-declared resolution.
{
"name": "booking flow",
"system_prompt": "You are a booking assistant.",
"defaults": {
"model": "anthropic.claude-opus-4.6",
"data_plane_id": "f79cbdae-4848-47ca-95e8-69588364d185",
"execution_cluster": "shared",
"max_iterations": 5,
"output_format_schema": {
"type": "object",
"additionalProperties": false,
"required": ["text"],
"properties": { "text": { "type": "string" } }
},
"tools": [{
"name": "confirm_booking",
"description": "Ask the user to confirm a proposed booking slot.",
"input_schema": {
"type": "object",
"additionalProperties": false,
"required": ["proposed_slot"],
"properties": { "proposed_slot": { "type": "string" } }
}
}]
}
}
Common 400 errors
type URL slug | Cause |
|---|---|
/errors/invalid-tool-alias | MCP alias doesn’t match ^[a-zA-Z][a-zA-Z0-9]{0,7}$ |
/errors/duplicate-tool-alias | Same alias appears twice across mcp_servers |
/errors/invalid-caller-tool-name | Caller-declared tools[].name is empty, contains a dash, or exceeds 64 chars |
/errors/duplicate-caller-tool-name | Same tools[].name appears twice |
/errors/tool-name-too-long | Some MCP wire name {alias}-{tool_name} > 64 chars |
See Agent Conversations error catalog for the full list.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
Create an empty conversation with a pinned system_prompt and a defaults block
that every subsequent run inherits (and may sparsely override via config_override).
The system_prompt is permanently fixed. Everything in defaults is, well, a
default — runs can replace any individual field.
The configuration applied to every run on this conversation by default. Each field
can be overridden per-run via config_override on the POST .../runs body, except
system_prompt which is fixed at conversation creation time.
For mcp_servers and tools, overrides replace the whole list, not individual
entries. There is no per-server or per-tool merge — set the complete catalog you want
for that run.
Optional human-readable label for your own use (logs, dashboards). The model never sees this.
"rate-limiting q&a"
"Stage direction" prepended to every run as a system-role message. Cannot be
overridden per-run — if you need different system prompts, create separate
conversations. Leaving it empty is allowed but rarely useful; even one line
("You are a helpful assistant. Answer concisely.") shapes model behavior.
"You are an AI assistant that answers questions about the Narrative Data Marketplace. Use the available tools to look things up before answering."
Response
Conversation created. Returns the persisted conversation row.
The conversation row as the API returns it. The same shape comes back from
POST /agents/conversations (201 Created) and GET /agents/conversations/{id} (200 OK).
UUID identifying a conversation. Returned from POST /agents/conversations and used
in every other agent endpoint that operates on this conversation.
"bc2505b7-068d-44ed-8055-a6f6ffe54ab1"
Company ID derived from the bearer token. Pinned for the conversation's lifetime.
1
User ID derived from the bearer token. Pinned for the conversation's lifetime and gates every subsequent read endpoint — only the originating user can fetch the conversation, its messages, or its runs. Peers in the same company see 404.
407
The configuration applied to every run on this conversation by default. Each field
can be overridden per-run via config_override on the POST .../runs body, except
system_prompt which is fixed at conversation creation time.
For mcp_servers and tools, overrides replace the whole list, not individual
entries. There is no per-server or per-tool merge — set the complete catalog you want
for that run.
Monotonic per-conversation counter, bumped by 1 (or more) every time a successful run appends messages. Two roles:
- Delta cursor for
GET .../messages?since=N— fetch only messages withsequence_no > N. - Compare-and-swap token for
POST .../runs— setexpected_versionto the conversation's current head; if it doesn't match at run-creation time, you get a Version Conflict (HTTP 409).
Always start a new run cycle by reading the current version from
GET /agents/conversations/{id} rather than caching a value from earlier.
x >= 04
"2026-05-18T13:21:30.355180Z"
"2026-05-18T13:21:51.983952Z"

