curl --request POST \
--url https://api-dev.narrative.io/agents/conversations/{id}/runs \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"client_op_id": "696b022c-e86d-4b3e-b6d7-ceb7eb1a495e",
"expected_version": 4,
"payload": {
"kind": "user_message",
"text": "Find me a meeting time for tomorrow afternoon.",
"content": [
{
"type": "text",
"text": "What does this chart say about Q3?"
},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
}
}
]
},
"tool_choice": {
"kind": "auto"
},
"config_override": {
"model": "anthropic.claude-opus-4.6",
"data_plane_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"compute_pool_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"max_iterations": 2,
"max_tokens": 2,
"temperature": 0.5,
"thinking": {
"type": "effort",
"level": "high"
},
"output_format_schema": {},
"mcp_servers": [
{
"alias": "docs",
"url": "https://docs.narrative.io/mcp",
"description": "Public Mintlify-hosted Narrative docs MCP server",
"connection_id": "b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b"
}
],
"tools": [
{
"name": "search_narrative_i_o_knowledge_base",
"description": "Search the Narrative.io documentation knowledge base.",
"input_schema": {
"type": "object",
"additionalProperties": false,
"required": [
"query"
],
"properties": {
"query": {
"type": "string"
}
}
},
"strict": true
}
]
}
}
'import requests
url = "https://api-dev.narrative.io/agents/conversations/{id}/runs"
payload = {
"client_op_id": "696b022c-e86d-4b3e-b6d7-ceb7eb1a495e",
"expected_version": 4,
"payload": {
"kind": "user_message",
"text": "Find me a meeting time for tomorrow afternoon.",
"content": [
{
"type": "text",
"text": "What does this chart say about Q3?"
},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
}
}
]
},
"tool_choice": { "kind": "auto" },
"config_override": {
"model": "anthropic.claude-opus-4.6",
"data_plane_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"compute_pool_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"max_iterations": 2,
"max_tokens": 2,
"temperature": 0.5,
"thinking": {
"type": "effort",
"level": "high"
},
"output_format_schema": {},
"mcp_servers": [
{
"alias": "docs",
"url": "https://docs.narrative.io/mcp",
"description": "Public Mintlify-hosted Narrative docs MCP server",
"connection_id": "b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b"
}
],
"tools": [
{
"name": "search_narrative_i_o_knowledge_base",
"description": "Search the Narrative.io documentation knowledge base.",
"input_schema": {
"type": "object",
"additionalProperties": False,
"required": ["query"],
"properties": { "query": { "type": "string" } }
},
"strict": True
}
]
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
client_op_id: '696b022c-e86d-4b3e-b6d7-ceb7eb1a495e',
expected_version: 4,
payload: {
kind: 'user_message',
text: 'Find me a meeting time for tomorrow afternoon.',
content: [
{type: 'text', text: 'What does this chart say about Q3?'},
{
type: 'image',
source: {
type: 'base64',
media_type: 'image/png',
data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='
}
}
]
},
tool_choice: {kind: 'auto'},
config_override: {
model: 'anthropic.claude-opus-4.6',
data_plane_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
compute_pool_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
max_iterations: 2,
max_tokens: 2,
temperature: 0.5,
thinking: {type: 'effort', level: 'high'},
output_format_schema: {},
mcp_servers: [
{
alias: 'docs',
url: 'https://docs.narrative.io/mcp',
description: 'Public Mintlify-hosted Narrative docs MCP server',
connection_id: 'b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b'
}
],
tools: [
{
name: 'search_narrative_i_o_knowledge_base',
description: 'Search the Narrative.io documentation knowledge base.',
input_schema: {
type: 'object',
additionalProperties: false,
required: ['query'],
properties: {query: {type: 'string'}}
},
strict: true
}
]
}
})
};
fetch('https://api-dev.narrative.io/agents/conversations/{id}/runs', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-dev.narrative.io/agents/conversations/{id}/runs",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'client_op_id' => '696b022c-e86d-4b3e-b6d7-ceb7eb1a495e',
'expected_version' => 4,
'payload' => [
'kind' => 'user_message',
'text' => 'Find me a meeting time for tomorrow afternoon.',
'content' => [
[
'type' => 'text',
'text' => 'What does this chart say about Q3?'
],
[
'type' => 'image',
'source' => [
'type' => 'base64',
'media_type' => 'image/png',
'data' => 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='
]
]
]
],
'tool_choice' => [
'kind' => 'auto'
],
'config_override' => [
'model' => 'anthropic.claude-opus-4.6',
'data_plane_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'compute_pool_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'max_iterations' => 2,
'max_tokens' => 2,
'temperature' => 0.5,
'thinking' => [
'type' => 'effort',
'level' => 'high'
],
'output_format_schema' => [
],
'mcp_servers' => [
[
'alias' => 'docs',
'url' => 'https://docs.narrative.io/mcp',
'description' => 'Public Mintlify-hosted Narrative docs MCP server',
'connection_id' => 'b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b'
]
],
'tools' => [
[
'name' => 'search_narrative_i_o_knowledge_base',
'description' => 'Search the Narrative.io documentation knowledge base.',
'input_schema' => [
'type' => 'object',
'additionalProperties' => false,
'required' => [
'query'
],
'properties' => [
'query' => [
'type' => 'string'
]
]
],
'strict' => true
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-dev.narrative.io/agents/conversations/{id}/runs"
payload := strings.NewReader("{\n \"client_op_id\": \"696b022c-e86d-4b3e-b6d7-ceb7eb1a495e\",\n \"expected_version\": 4,\n \"payload\": {\n \"kind\": \"user_message\",\n \"text\": \"Find me a meeting time for tomorrow afternoon.\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"What does this chart say about Q3?\"\n },\n {\n \"type\": \"image\",\n \"source\": {\n \"type\": \"base64\",\n \"media_type\": \"image/png\",\n \"data\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==\"\n }\n }\n ]\n },\n \"tool_choice\": {\n \"kind\": \"auto\"\n },\n \"config_override\": {\n \"model\": \"anthropic.claude-opus-4.6\",\n \"data_plane_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"compute_pool_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"max_iterations\": 2,\n \"max_tokens\": 2,\n \"temperature\": 0.5,\n \"thinking\": {\n \"type\": \"effort\",\n \"level\": \"high\"\n },\n \"output_format_schema\": {},\n \"mcp_servers\": [\n {\n \"alias\": \"docs\",\n \"url\": \"https://docs.narrative.io/mcp\",\n \"description\": \"Public Mintlify-hosted Narrative docs MCP server\",\n \"connection_id\": \"b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b\"\n }\n ],\n \"tools\": [\n {\n \"name\": \"search_narrative_i_o_knowledge_base\",\n \"description\": \"Search the Narrative.io documentation knowledge base.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\n \"query\"\n ],\n \"properties\": {\n \"query\": {\n \"type\": \"string\"\n }\n }\n },\n \"strict\": true\n }\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-dev.narrative.io/agents/conversations/{id}/runs")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"client_op_id\": \"696b022c-e86d-4b3e-b6d7-ceb7eb1a495e\",\n \"expected_version\": 4,\n \"payload\": {\n \"kind\": \"user_message\",\n \"text\": \"Find me a meeting time for tomorrow afternoon.\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"What does this chart say about Q3?\"\n },\n {\n \"type\": \"image\",\n \"source\": {\n \"type\": \"base64\",\n \"media_type\": \"image/png\",\n \"data\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==\"\n }\n }\n ]\n },\n \"tool_choice\": {\n \"kind\": \"auto\"\n },\n \"config_override\": {\n \"model\": \"anthropic.claude-opus-4.6\",\n \"data_plane_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"compute_pool_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"max_iterations\": 2,\n \"max_tokens\": 2,\n \"temperature\": 0.5,\n \"thinking\": {\n \"type\": \"effort\",\n \"level\": \"high\"\n },\n \"output_format_schema\": {},\n \"mcp_servers\": [\n {\n \"alias\": \"docs\",\n \"url\": \"https://docs.narrative.io/mcp\",\n \"description\": \"Public Mintlify-hosted Narrative docs MCP server\",\n \"connection_id\": \"b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b\"\n }\n ],\n \"tools\": [\n {\n \"name\": \"search_narrative_i_o_knowledge_base\",\n \"description\": \"Search the Narrative.io documentation knowledge base.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\n \"query\"\n ],\n \"properties\": {\n \"query\": {\n \"type\": \"string\"\n }\n }\n },\n \"strict\": true\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-dev.narrative.io/agents/conversations/{id}/runs")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"client_op_id\": \"696b022c-e86d-4b3e-b6d7-ceb7eb1a495e\",\n \"expected_version\": 4,\n \"payload\": {\n \"kind\": \"user_message\",\n \"text\": \"Find me a meeting time for tomorrow afternoon.\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"What does this chart say about Q3?\"\n },\n {\n \"type\": \"image\",\n \"source\": {\n \"type\": \"base64\",\n \"media_type\": \"image/png\",\n \"data\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==\"\n }\n }\n ]\n },\n \"tool_choice\": {\n \"kind\": \"auto\"\n },\n \"config_override\": {\n \"model\": \"anthropic.claude-opus-4.6\",\n \"data_plane_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"compute_pool_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"max_iterations\": 2,\n \"max_tokens\": 2,\n \"temperature\": 0.5,\n \"thinking\": {\n \"type\": \"effort\",\n \"level\": \"high\"\n },\n \"output_format_schema\": {},\n \"mcp_servers\": [\n {\n \"alias\": \"docs\",\n \"url\": \"https://docs.narrative.io/mcp\",\n \"description\": \"Public Mintlify-hosted Narrative docs MCP server\",\n \"connection_id\": \"b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b\"\n }\n ],\n \"tools\": [\n {\n \"name\": \"search_narrative_i_o_knowledge_base\",\n \"description\": \"Search the Narrative.io documentation knowledge base.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\n \"query\"\n ],\n \"properties\": {\n \"query\": {\n \"type\": \"string\"\n }\n }\n },\n \"strict\": true\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "82eb9cb7-619e-46bb-ac1c-8a32b0112c1c",
"conversation_id": "bc2505b7-068d-44ed-8055-a6f6ffe54ab1",
"company_id": 1,
"user_id": 407,
"client_op_id": "696b022c-e86d-4b3e-b6d7-ceb7eb1a495e",
"status": "pending",
"effective_config": {
"model": "anthropic.claude-opus-4.6",
"data_plane_id": "f79cbdae-4848-47ca-95e8-69588364d185",
"execution_cluster": "shared",
"compute_pool_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"max_iterations": 8,
"max_tokens": 2048,
"temperature": 0,
"thinking": {
"type": "effort",
"level": "high"
},
"output_format_schema": {
"type": "object",
"additionalProperties": false,
"required": [
"text"
],
"properties": {
"text": {
"type": "string"
}
}
},
"mcp_servers": [],
"tools": []
},
"submitted_inference_job_ids": [
"3c90c3cc-0d44-4b50-8888-8dd25736052a"
],
"pending_tool_calls": [
{
"tool_use_id": "tooluse_DWXPKZ50JDGib5GmShyUgJ",
"name": "confirm_booking",
"arguments": {
"proposed_slot": "tomorrow afternoon"
}
}
],
"started_at": "2026-05-18T13:21:30.355180Z",
"live": {
"messages": [
{
"turn_index": 0,
"role": "user",
"content_blocks": [
{
"type": "text",
"text": "What is 2 + 2?"
}
],
"final_answer_from": []
}
],
"current_name": "Tomorrow Afternoon Meeting"
},
"tool_choice": {
"kind": "auto"
},
"iterations_used": 6,
"usage": {
"prompt_tokens": 14842,
"completion_tokens": 510,
"total_tokens": 15352
},
"final_text": "<string>",
"final_structured_output": "<unknown>",
"error": {
"type": "AgentLoopMaxIterationsExceeded",
"message": "max iterations exceeded after 5 iterations",
"title": "Max Iterations Exceeded",
"docs_url": "https://docs.narrative.io/reference/architecture/agent-conversations/errors/max-iterations-exceeded"
},
"completed_at": "2023-11-07T05:31:56Z"
}{
"type": "https://docs.narrative.io/reference/architecture/agent-conversations/errors/invalid-tool-alias",
"title": "Invalid Tool Alias",
"status": 400,
"detail": "invalid tool aliases (must match ^[a-zA-Z][a-zA-Z0-9]{0,7}$): with_underscore",
"instance": null,
"log_id": "1f6e5d4c-3b2a-1098-7654-3210fedcba98",
"debug": null
}{
"error": "Unauthorized",
"error_description": "You are not authorized to use this endpoint."
}{
"type": "https://docs.narrative.io/reference/architecture/agent-conversations/errors/invalid-tool-alias",
"title": "Invalid Tool Alias",
"status": 400,
"detail": "invalid tool aliases (must match ^[a-zA-Z][a-zA-Z0-9]{0,7}$): with_underscore",
"instance": null,
"log_id": "1f6e5d4c-3b2a-1098-7654-3210fedcba98",
"debug": null
}{
"type": "https://docs.narrative.io/reference/architecture/agent-conversations/errors/invalid-tool-alias",
"title": "Invalid Tool Alias",
"status": 400,
"detail": "invalid tool aliases (must match ^[a-zA-Z][a-zA-Z0-9]{0,7}$): with_underscore",
"instance": null,
"log_id": "1f6e5d4c-3b2a-1098-7654-3210fedcba98",
"debug": null
}{
"type": "https://docs.narrative.io/reference/architecture/agent-conversations/errors/invalid-tool-alias",
"title": "Invalid Tool Alias",
"status": 400,
"detail": "invalid tool aliases (must match ^[a-zA-Z][a-zA-Z0-9]{0,7}$): with_underscore",
"instance": null,
"log_id": "1f6e5d4c-3b2a-1098-7654-3210fedcba98",
"debug": null
}Start a new run on the conversation
Inserts a row in agent_runs, kicks off the workflow that drives the agent loop, and
returns immediately. The response has status: "pending" — poll
GET /agents/runs/{id} to track progress.
Runs are asynchronous. This endpoint does not wait for the model to reply. Typical end-to-end latency is 3–10 seconds for a single-iteration run, longer for multi-iteration tool-use loops (60–120 seconds is common for 6–8 iterations against an MCP server).
Required fields
client_op_id— UUID you generate. Used as the idempotency key, scoped to(company_id, user_id, client_op_id). Re-sending the same value returns the original run row unchanged, whichever conversation that run is on. Always generate a fresh UUID for a fresh logical request; reusing one is only safe if the second call is a retry of the first.expected_version— the conversation’s current version, read fromGET /agents/conversations/{id}just before this call. If it doesn’t match at run-creation time, you get a 409 Version Conflict. Recovery: refetch the conversation, re-readversion, retry the run with the fresh value.payload—kind: "user_message"to start a fresh user turn, orkind: "tool_outputs"to resume a previously-paused (requires_action) run.
Optional fields
tool_choice— bias the model toward (or away from) using tools on iteration 1 of this run. Three values:auto,any,specific_tool(see schema). Applies only to iteration 1 of this run; iteration 2+ are back onauto. To re-force a specific tool on the next run, settool_choiceagain on its request body.config_override— sparse override of the conversation’sdefaults. Any field you set replaces the conversation-level value for this single run. The two list fields (mcp_servers,tools) are wholesale-replaced, not merged. Thesystem_promptcannot be overridden.
Run lifecycle (state diagram)
pending → running → completed ← model produced a final answer
↘ requires_action ← model called a caller-declared tool
↘ failed ← non-recoverable error
You stop polling on any of the three terminal states. For requires_action, the run
can be resumed by posting a new run on the same conversation with
payload.kind: "tool_outputs". For completed and failed, the run is done.
One active run per conversation
A conversation runs one turn at a time. While a run is pending or running, posting
another run on the same conversation returns a 409
Run Already In Progress.
The detail names the active run’s id — and, when the run has produced a live turn,
what it is currently doing (for example waiting on a Narrative job).
Recovery: poll GET /agents/runs/{active_run_id} until it reaches a terminal state,
re-read the conversation’s version, then post your run. A run parked on a long job
can stay non-terminal for a while; live.messages on the run response shows what it is
waiting for.
The three terminal states do not block a new run — in particular a
requires_action run is done, so the run that resumes it with tool_outputs is
accepted as normal.
Re-posting a client_op_id that already has a run on this conversation is not
refused: you get that run back, even while a different run is active. The bypass is
per-conversation, while idempotency itself is per-owner — so a client_op_id whose
run lives on another conversation does not bypass this check, and you get the 409
while a run here is active. Let the active run finish and re-post; you will then get
the original run back as usual.
Examples
Example 1 — fresh user message, no tools
{
"client_op_id": "696b022c-e86d-4b3e-b6d7-ceb7eb1a495e",
"expected_version": 0,
"payload": { "kind": "user_message", "text": "What is 2 + 2?" }
}
Returns:
{
"id": "82eb9cb7-619e-46bb-ac1c-8a32b0112c1c",
"status": "pending",
"started_at": "2026-05-18T13:21:30.355180Z",
"submitted_inference_job_ids": [],
"pending_tool_calls": [],
"effective_config": { ... },
...
}
Then poll GET /agents/runs/{id} until status: completed. Final
final_text: "4".
Example 2 — force a specific tool, override max_iterations
{
"client_op_id": "c8aef867-eca6-46f7-8a99-e22dd0ee6062",
"expected_version": 0,
"payload": { "kind": "user_message", "text": "Find me a meeting time for tomorrow afternoon." },
"tool_choice": { "kind": "specific_tool", "name": "confirm_booking" },
"config_override": { "max_iterations": 10 }
}
The model is forced to call confirm_booking on iteration 1. Since confirm_booking
is a caller-declared tool (dash-free name, listed in tools[]), the run terminates
with status: requires_action after one iteration; pending_tool_calls contains the
model’s proposed arguments.
To pin an MCP-resolved tool instead, include the mcp_alias field:
"tool_choice": { "kind": "specific_tool", "mcp_alias": "docs", "name": "search_kb" }
Example 3 — resume a paused run
First, refetch the conversation to get the new version (it has advanced because the paused run’s assistant turn was persisted):
curl "$API/agents/conversations/{id}" -H "Authorization: Bearer $TOKEN"
# → { "version": 2, ... }
Then post the resume:
{
"client_op_id": "7fb6795c-60a2-4cf9-a0db-0cc645279704",
"expected_version": 2,
"payload": {
"kind": "tool_outputs",
"outputs": [{
"tool_use_id": "tooluse_DWXPKZ50JDGib5GmShyUgJ",
"content": "User confirmed the proposed slot.",
"is_error": false
}]
}
}
The model sees the tool result and continues; this run usually terminates
completed after one iteration with a contextual final answer.
Idempotency
Re-sending the same body with the same client_op_id returns the original
agent_runs row, with a 202 like the first call. Idempotency is on
(company_id, user_id, client_op_id):
- Same
client_op_id, same conversation → same run row returned, no new workflow started. - Same
client_op_id, different conversation → the original run is returned again, and no run is created on the conversation you posted to. The response body’sconversation_idis the one the original run belongs to, so check it if you reuse ids.
The conversation checks run first, so a retry only gets the original run back while
expected_version still matches. Once that run finishes and its messages land, the
conversation’s version moves on and the same retry gets a 409 Version Conflict. Read the
original run with GET /agents/runs/{id} instead.
Use a fresh UUID per logical request, never per retry of an in-flight request.
Common error responses
| Status | type slug | Cause | Recovery |
|---|---|---|---|
| 400 | /invalid-tool-alias | config_override.mcp_servers[] has a bad alias | Fix the alias |
| 400 | /tool-name-too-long | config_override introduced a wire name > 64 chars | Shorten alias or tool name |
| 400 | /unknown-tool-use-id | tool_outputs references an id not on the latest assistant turn | Refetch the prior run’s pending_tool_calls |
| 400 | /not-a-client-tool-call | tool_outputs references a server-resolved call | Filter to client-side ids only |
| 400 | /incomplete-tool-outputs | Missing or extra outputs[] entries | Match the pending set exactly |
| 400 | /no-assistant-turn | tool_outputs payload on a conversation with no assistant turn yet | Use user_message payload instead |
| 400 | /unknown-tool-choice-name | tool_choice.name is not a declared tool | Fix the name |
| 400 | /unknown-tool-choice-mcp-alias | tool_choice.mcp_alias is not one of the run’s MCP servers | Fix the alias |
| 400 | /thinking-with-forced-tool-choice | specific_tool or any combined with thinking | Drop one of them |
| 400 | /forced-tool-choice-not-supported | The model can’t be forced to call a tool | Use auto, or another model |
| 403 | none (type is null) | The company is over its credit limit, or has no credit account | Contact your account manager |
| 404 | /conversation-not-found | The conversation was not found | Verify the id |
| 409 | /version-conflict | expected_version mismatch | Refetch the conversation, retry |
| 409 | /run-in-progress | Another run on this conversation is pending or running | Poll the active run named in detail until terminal, then retry |
| 500 | /workflow-start-failed | Platform couldn’t reach the workflow engine | Retry with the same client_op_id |
Permission
agent_conversations resource with write verb.
curl --request POST \
--url https://api-dev.narrative.io/agents/conversations/{id}/runs \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"client_op_id": "696b022c-e86d-4b3e-b6d7-ceb7eb1a495e",
"expected_version": 4,
"payload": {
"kind": "user_message",
"text": "Find me a meeting time for tomorrow afternoon.",
"content": [
{
"type": "text",
"text": "What does this chart say about Q3?"
},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
}
}
]
},
"tool_choice": {
"kind": "auto"
},
"config_override": {
"model": "anthropic.claude-opus-4.6",
"data_plane_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"compute_pool_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"max_iterations": 2,
"max_tokens": 2,
"temperature": 0.5,
"thinking": {
"type": "effort",
"level": "high"
},
"output_format_schema": {},
"mcp_servers": [
{
"alias": "docs",
"url": "https://docs.narrative.io/mcp",
"description": "Public Mintlify-hosted Narrative docs MCP server",
"connection_id": "b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b"
}
],
"tools": [
{
"name": "search_narrative_i_o_knowledge_base",
"description": "Search the Narrative.io documentation knowledge base.",
"input_schema": {
"type": "object",
"additionalProperties": false,
"required": [
"query"
],
"properties": {
"query": {
"type": "string"
}
}
},
"strict": true
}
]
}
}
'import requests
url = "https://api-dev.narrative.io/agents/conversations/{id}/runs"
payload = {
"client_op_id": "696b022c-e86d-4b3e-b6d7-ceb7eb1a495e",
"expected_version": 4,
"payload": {
"kind": "user_message",
"text": "Find me a meeting time for tomorrow afternoon.",
"content": [
{
"type": "text",
"text": "What does this chart say about Q3?"
},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
}
}
]
},
"tool_choice": { "kind": "auto" },
"config_override": {
"model": "anthropic.claude-opus-4.6",
"data_plane_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"compute_pool_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"max_iterations": 2,
"max_tokens": 2,
"temperature": 0.5,
"thinking": {
"type": "effort",
"level": "high"
},
"output_format_schema": {},
"mcp_servers": [
{
"alias": "docs",
"url": "https://docs.narrative.io/mcp",
"description": "Public Mintlify-hosted Narrative docs MCP server",
"connection_id": "b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b"
}
],
"tools": [
{
"name": "search_narrative_i_o_knowledge_base",
"description": "Search the Narrative.io documentation knowledge base.",
"input_schema": {
"type": "object",
"additionalProperties": False,
"required": ["query"],
"properties": { "query": { "type": "string" } }
},
"strict": True
}
]
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
client_op_id: '696b022c-e86d-4b3e-b6d7-ceb7eb1a495e',
expected_version: 4,
payload: {
kind: 'user_message',
text: 'Find me a meeting time for tomorrow afternoon.',
content: [
{type: 'text', text: 'What does this chart say about Q3?'},
{
type: 'image',
source: {
type: 'base64',
media_type: 'image/png',
data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='
}
}
]
},
tool_choice: {kind: 'auto'},
config_override: {
model: 'anthropic.claude-opus-4.6',
data_plane_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
compute_pool_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
max_iterations: 2,
max_tokens: 2,
temperature: 0.5,
thinking: {type: 'effort', level: 'high'},
output_format_schema: {},
mcp_servers: [
{
alias: 'docs',
url: 'https://docs.narrative.io/mcp',
description: 'Public Mintlify-hosted Narrative docs MCP server',
connection_id: 'b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b'
}
],
tools: [
{
name: 'search_narrative_i_o_knowledge_base',
description: 'Search the Narrative.io documentation knowledge base.',
input_schema: {
type: 'object',
additionalProperties: false,
required: ['query'],
properties: {query: {type: 'string'}}
},
strict: true
}
]
}
})
};
fetch('https://api-dev.narrative.io/agents/conversations/{id}/runs', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-dev.narrative.io/agents/conversations/{id}/runs",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'client_op_id' => '696b022c-e86d-4b3e-b6d7-ceb7eb1a495e',
'expected_version' => 4,
'payload' => [
'kind' => 'user_message',
'text' => 'Find me a meeting time for tomorrow afternoon.',
'content' => [
[
'type' => 'text',
'text' => 'What does this chart say about Q3?'
],
[
'type' => 'image',
'source' => [
'type' => 'base64',
'media_type' => 'image/png',
'data' => 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='
]
]
]
],
'tool_choice' => [
'kind' => 'auto'
],
'config_override' => [
'model' => 'anthropic.claude-opus-4.6',
'data_plane_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'compute_pool_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'max_iterations' => 2,
'max_tokens' => 2,
'temperature' => 0.5,
'thinking' => [
'type' => 'effort',
'level' => 'high'
],
'output_format_schema' => [
],
'mcp_servers' => [
[
'alias' => 'docs',
'url' => 'https://docs.narrative.io/mcp',
'description' => 'Public Mintlify-hosted Narrative docs MCP server',
'connection_id' => 'b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b'
]
],
'tools' => [
[
'name' => 'search_narrative_i_o_knowledge_base',
'description' => 'Search the Narrative.io documentation knowledge base.',
'input_schema' => [
'type' => 'object',
'additionalProperties' => false,
'required' => [
'query'
],
'properties' => [
'query' => [
'type' => 'string'
]
]
],
'strict' => true
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-dev.narrative.io/agents/conversations/{id}/runs"
payload := strings.NewReader("{\n \"client_op_id\": \"696b022c-e86d-4b3e-b6d7-ceb7eb1a495e\",\n \"expected_version\": 4,\n \"payload\": {\n \"kind\": \"user_message\",\n \"text\": \"Find me a meeting time for tomorrow afternoon.\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"What does this chart say about Q3?\"\n },\n {\n \"type\": \"image\",\n \"source\": {\n \"type\": \"base64\",\n \"media_type\": \"image/png\",\n \"data\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==\"\n }\n }\n ]\n },\n \"tool_choice\": {\n \"kind\": \"auto\"\n },\n \"config_override\": {\n \"model\": \"anthropic.claude-opus-4.6\",\n \"data_plane_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"compute_pool_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"max_iterations\": 2,\n \"max_tokens\": 2,\n \"temperature\": 0.5,\n \"thinking\": {\n \"type\": \"effort\",\n \"level\": \"high\"\n },\n \"output_format_schema\": {},\n \"mcp_servers\": [\n {\n \"alias\": \"docs\",\n \"url\": \"https://docs.narrative.io/mcp\",\n \"description\": \"Public Mintlify-hosted Narrative docs MCP server\",\n \"connection_id\": \"b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b\"\n }\n ],\n \"tools\": [\n {\n \"name\": \"search_narrative_i_o_knowledge_base\",\n \"description\": \"Search the Narrative.io documentation knowledge base.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\n \"query\"\n ],\n \"properties\": {\n \"query\": {\n \"type\": \"string\"\n }\n }\n },\n \"strict\": true\n }\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-dev.narrative.io/agents/conversations/{id}/runs")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"client_op_id\": \"696b022c-e86d-4b3e-b6d7-ceb7eb1a495e\",\n \"expected_version\": 4,\n \"payload\": {\n \"kind\": \"user_message\",\n \"text\": \"Find me a meeting time for tomorrow afternoon.\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"What does this chart say about Q3?\"\n },\n {\n \"type\": \"image\",\n \"source\": {\n \"type\": \"base64\",\n \"media_type\": \"image/png\",\n \"data\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==\"\n }\n }\n ]\n },\n \"tool_choice\": {\n \"kind\": \"auto\"\n },\n \"config_override\": {\n \"model\": \"anthropic.claude-opus-4.6\",\n \"data_plane_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"compute_pool_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"max_iterations\": 2,\n \"max_tokens\": 2,\n \"temperature\": 0.5,\n \"thinking\": {\n \"type\": \"effort\",\n \"level\": \"high\"\n },\n \"output_format_schema\": {},\n \"mcp_servers\": [\n {\n \"alias\": \"docs\",\n \"url\": \"https://docs.narrative.io/mcp\",\n \"description\": \"Public Mintlify-hosted Narrative docs MCP server\",\n \"connection_id\": \"b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b\"\n }\n ],\n \"tools\": [\n {\n \"name\": \"search_narrative_i_o_knowledge_base\",\n \"description\": \"Search the Narrative.io documentation knowledge base.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\n \"query\"\n ],\n \"properties\": {\n \"query\": {\n \"type\": \"string\"\n }\n }\n },\n \"strict\": true\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-dev.narrative.io/agents/conversations/{id}/runs")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"client_op_id\": \"696b022c-e86d-4b3e-b6d7-ceb7eb1a495e\",\n \"expected_version\": 4,\n \"payload\": {\n \"kind\": \"user_message\",\n \"text\": \"Find me a meeting time for tomorrow afternoon.\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"What does this chart say about Q3?\"\n },\n {\n \"type\": \"image\",\n \"source\": {\n \"type\": \"base64\",\n \"media_type\": \"image/png\",\n \"data\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==\"\n }\n }\n ]\n },\n \"tool_choice\": {\n \"kind\": \"auto\"\n },\n \"config_override\": {\n \"model\": \"anthropic.claude-opus-4.6\",\n \"data_plane_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"compute_pool_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"max_iterations\": 2,\n \"max_tokens\": 2,\n \"temperature\": 0.5,\n \"thinking\": {\n \"type\": \"effort\",\n \"level\": \"high\"\n },\n \"output_format_schema\": {},\n \"mcp_servers\": [\n {\n \"alias\": \"docs\",\n \"url\": \"https://docs.narrative.io/mcp\",\n \"description\": \"Public Mintlify-hosted Narrative docs MCP server\",\n \"connection_id\": \"b3f1c2a4-5d6e-47f8-9a0b-1c2d3e4f5a6b\"\n }\n ],\n \"tools\": [\n {\n \"name\": \"search_narrative_i_o_knowledge_base\",\n \"description\": \"Search the Narrative.io documentation knowledge base.\",\n \"input_schema\": {\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\n \"query\"\n ],\n \"properties\": {\n \"query\": {\n \"type\": \"string\"\n }\n }\n },\n \"strict\": true\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "82eb9cb7-619e-46bb-ac1c-8a32b0112c1c",
"conversation_id": "bc2505b7-068d-44ed-8055-a6f6ffe54ab1",
"company_id": 1,
"user_id": 407,
"client_op_id": "696b022c-e86d-4b3e-b6d7-ceb7eb1a495e",
"status": "pending",
"effective_config": {
"model": "anthropic.claude-opus-4.6",
"data_plane_id": "f79cbdae-4848-47ca-95e8-69588364d185",
"execution_cluster": "shared",
"compute_pool_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"max_iterations": 8,
"max_tokens": 2048,
"temperature": 0,
"thinking": {
"type": "effort",
"level": "high"
},
"output_format_schema": {
"type": "object",
"additionalProperties": false,
"required": [
"text"
],
"properties": {
"text": {
"type": "string"
}
}
},
"mcp_servers": [],
"tools": []
},
"submitted_inference_job_ids": [
"3c90c3cc-0d44-4b50-8888-8dd25736052a"
],
"pending_tool_calls": [
{
"tool_use_id": "tooluse_DWXPKZ50JDGib5GmShyUgJ",
"name": "confirm_booking",
"arguments": {
"proposed_slot": "tomorrow afternoon"
}
}
],
"started_at": "2026-05-18T13:21:30.355180Z",
"live": {
"messages": [
{
"turn_index": 0,
"role": "user",
"content_blocks": [
{
"type": "text",
"text": "What is 2 + 2?"
}
],
"final_answer_from": []
}
],
"current_name": "Tomorrow Afternoon Meeting"
},
"tool_choice": {
"kind": "auto"
},
"iterations_used": 6,
"usage": {
"prompt_tokens": 14842,
"completion_tokens": 510,
"total_tokens": 15352
},
"final_text": "<string>",
"final_structured_output": "<unknown>",
"error": {
"type": "AgentLoopMaxIterationsExceeded",
"message": "max iterations exceeded after 5 iterations",
"title": "Max Iterations Exceeded",
"docs_url": "https://docs.narrative.io/reference/architecture/agent-conversations/errors/max-iterations-exceeded"
},
"completed_at": "2023-11-07T05:31:56Z"
}{
"type": "https://docs.narrative.io/reference/architecture/agent-conversations/errors/invalid-tool-alias",
"title": "Invalid Tool Alias",
"status": 400,
"detail": "invalid tool aliases (must match ^[a-zA-Z][a-zA-Z0-9]{0,7}$): with_underscore",
"instance": null,
"log_id": "1f6e5d4c-3b2a-1098-7654-3210fedcba98",
"debug": null
}{
"error": "Unauthorized",
"error_description": "You are not authorized to use this endpoint."
}{
"type": "https://docs.narrative.io/reference/architecture/agent-conversations/errors/invalid-tool-alias",
"title": "Invalid Tool Alias",
"status": 400,
"detail": "invalid tool aliases (must match ^[a-zA-Z][a-zA-Z0-9]{0,7}$): with_underscore",
"instance": null,
"log_id": "1f6e5d4c-3b2a-1098-7654-3210fedcba98",
"debug": null
}{
"type": "https://docs.narrative.io/reference/architecture/agent-conversations/errors/invalid-tool-alias",
"title": "Invalid Tool Alias",
"status": 400,
"detail": "invalid tool aliases (must match ^[a-zA-Z][a-zA-Z0-9]{0,7}$): with_underscore",
"instance": null,
"log_id": "1f6e5d4c-3b2a-1098-7654-3210fedcba98",
"debug": null
}{
"type": "https://docs.narrative.io/reference/architecture/agent-conversations/errors/invalid-tool-alias",
"title": "Invalid Tool Alias",
"status": 400,
"detail": "invalid tool aliases (must match ^[a-zA-Z][a-zA-Z0-9]{0,7}$): with_underscore",
"instance": null,
"log_id": "1f6e5d4c-3b2a-1098-7654-3210fedcba98",
"debug": null
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
The conversation's UUID.
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"
Body
Start a new run on the conversation.
Required fields:
client_op_id— your-generated UUID for idempotency.expected_version— the conversation's current version, read just before this call. Triggers a 409 Version Conflict if the version moved.payload— the run's input.
Optional fields:
tool_choice— biases tool selection on iteration 1 of this run only.config_override— sparse override of the conversation'sdefaultsfor this run.
A UUID you generate for each POST .../runs call. Acts as an idempotency key:
re-sending the same client_op_id returns the original run row unchanged. This lets
you retry network blips, request timeouts, etc. without accidentally starting a
second workflow.
Scope: (company_id, user_id, client_op_id) is the uniqueness key. One
client_op_id means one run for you, on whichever conversation it first landed —
posting it again against a different conversation returns that first run rather than
starting a new one.
"696b022c-e86d-4b3e-b6d7-ceb7eb1a495e"
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
The run's input. Discriminated by kind:
user_message— fresh user turn.tool_outputs— resuming a paused run with the tool answers.
- Option 1
- Option 2
Show child attributes
Show child attributes
Per-run policy that biases the model toward (or away from) using tools on the first
iteration of the run. From iteration 2 onward the model is back on {"kind": "auto"} regardless.
Per-run, not per-conversation: every new run picks its own tool_choice. To
re-force a particular tool on every follow-up run (for example, always asking the user
for confirmation), set it on each POST .../runs body.
Three shapes, discriminated by kind:
- Auto
- Any
- SpecificTool
Show child attributes
Show child attributes
{ "kind": "auto" }
Sparse override applied to a single run. Any field set here replaces the
corresponding defaults field for that run only. Fields not set inherit from
defaults.
Two list fields (mcp_servers, tools) replace the whole list when set, not
merge — if you provide them in the override, the conversation defaults' lists are
ignored entirely for that run.
Every field may also be sent as null, which means the same as leaving it out: the run
inherits the conversation's value. A null cannot clear a default.
system_prompt is not in this object — it's pinned at conversation creation and
cannot be overridden.
Show child attributes
Show child attributes
Response
Run accepted; workflow is starting. Body carries the run row, initially with
status: "pending". Poll GET /agents/runs/{id} for terminal state.
An idempotent retry (a client_op_id posted before) also gets a 202, carrying the
original run, which may have moved past pending.
Run state as returned by POST /agents/conversations/{id}/runs (Accepted, 202) and
GET /agents/runs/{id} (OK, 200). Which optional fields are populated depends on
status:
completed→iterations_used,usage,submitted_inference_job_ids, and exactly one offinal_text/final_structured_output. The two are mutually exclusive: text-mode runs (nooutput_format_schemasupplied) populatefinal_text; structured-mode runs populatefinal_structured_outputwith the caller-schema-conforming JSON.requires_action→pending_tool_calls,iterations_used,usage,submitted_inference_job_ids.failed→error, possibly partialiterations_used/usage/submitted_inference_job_ids.pending/running→ only the at-creation fields (id,conversation_id,started_at, etc.) plussubmitted_inference_job_idsif any iterations have started.
UUID identifying a single run. Returned by POST /agents/conversations/{id}/runs and
used in GET /agents/runs/{id} to poll progress.
"82eb9cb7-619e-46bb-ac1c-8a32b0112c1c"
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"
1
407
A UUID you generate for each POST .../runs call. Acts as an idempotency key:
re-sending the same client_op_id returns the original run row unchanged. This lets
you retry network blips, request timeouts, etc. without accidentally starting a
second workflow.
Scope: (company_id, user_id, client_op_id) is the uniqueness key. One
client_op_id means one run for you, on whichever conversation it first landed —
posting it again against a different conversation returns that first run rather than
starting a new one.
"696b022c-e86d-4b3e-b6d7-ceb7eb1a495e"
Where the run is in its lifecycle. Three terminal states (completed,
requires_action, failed) — you stop polling when you see any of them. The two
in-flight states (pending, running) mean "keep polling."
pending— run row inserted by the API but the workflow hasn't picked it up yet. Usually < 1 second; if a run stays here for tens of seconds the workflow workers may be down.running— workflow is mid-execution. One "running" you see roughly corresponds to one inference iteration; you might cycle through this state several times for a multi-iteration tool-use loop.completed— model produced a final answer. Exactly one offinal_text/final_structured_outputis populated depending on whether the caller supplied anoutput_format_schemaon the conversation defaults or run override.requires_action— model called a client-side tool.pending_tool_callsis populated; resume by posting a new run withpayload.kind: tool_outputs.failed— non-recoverable error.error.type,error.message,error.title,error.docs_urlare populated. See the error catalog for the per-type meaning. A run that was deliberately cancelled also lands here, tagged witherror.type: AgentLoopCancelledso it is distinguishable from a genuine failure.
pending, running, completed, requires_action, failed The fully-merged config that was used to run this turn — conversation defaults
with config_override applied. Echoed back so you can see exactly what the model
saw, especially helpful when debugging unexpected behavior caused by overrides.
Show child attributes
Show child attributes
Inference job IDs (one per iteration) for cross-system tracing. Each ID is a row
in the platform's jobs table — use it to pull the raw model request/response
if you need to debug a specific iteration.
Tool calls the run is waiting for the caller to answer. Empty unless status is
requires_action.
Show child attributes
Show child attributes
"2026-05-18T13:21:30.355180Z"
The conversation's current, still-mutating state (e.g. its auto-generated name).
Populated on every read, independent of status.
Show child attributes
Show child attributes
The tool_choice the run was started with, or null if it had none.
- Auto
- Any
- SpecificTool
Show child attributes
Show child attributes
{ "kind": "auto" }
Number of inference iterations this run completed. Populated on terminal states.
Equal to submitted_inference_job_ids.length for the same run.
x >= 06
Token usage totals across every inference iteration in this run. Used for billing
and capacity planning. Note that prompt_tokens grows with each iteration because
each round re-sends the full conversation history to the model.
Show child attributes
Show child attributes
The model's final answer in plain text. Populated on status: completed when the
caller did NOT supply an output_format_schema (text mode). Mutually exclusive with
final_structured_output.
The model's final answer as a JSON object conforming to the caller's
output_format_schema. Populated on status: completed when the caller supplied an
output_format_schema on the conversation defaults or run override (structured
mode). The shape is the caller's schema verbatim — no top-level text field is
required or implied. Mutually exclusive with final_text.
Populated only on status: failed.
Show child attributes
Show child attributes
When the run reached a terminal status. Null while in pending or running.
Was this page helpful?

