Returns the run’s current state — status, iterations used so far, token usage, submitted inference job IDs, the model’s final answer (if completed), pending tool calls (if requires_action), or the error (if failed).
Runs are asynchronous. After POST /agents/conversations/{id}/runs you poll this
endpoint until you see a terminal status (completed, requires_action, or
failed). A reasonable polling cadence:
submitted_inference_job_ids in the data plane’s jobs table) or
the workflow timed out and will land failed shortly.status | Populated fields |
|---|---|
pending | id, conversation_id, client_op_id, effective_config, started_at |
running | above + submitted_inference_job_ids (one per iteration completed) |
completed | above + iterations_used, usage, final_text, completed_at |
requires_action | above + iterations_used, usage, pending_tool_calls, completed_at |
failed | above + error, possibly partial iterations_used/usage, completed_at |
effective_config is always populated — it’s the fully-merged config used for
this run (conversation defaults + this run’s config_override). Helpful for
confirming the model saw what you expected.
Each run runs N iterations, where N ≤ effective_config.max_iterations. Each
iteration is one inference job submitted to the data plane (one Bedrock or Cortex
API call). submitted_inference_job_ids carries the platform’s jobs.id for each
iteration — use those to drill into the raw model request/response in the data
plane’s logs / job table.
iterations_used and submitted_inference_job_ids.length match for any terminal
state.
On status: "failed" the error field carries:
type — opaque incident code (e.g. AgentLoopMaxIterationsExceeded,
AgentLoopInvalidEffectiveConfig, UnknownTool). Stable across versions, used
for log correlation.message — human-readable detail.title and docs_url — caller-facing presentation looked up from the platform’s
error catalog.
Absent if the API doesn’t recognize the type (usually because the workflow
workers have been upgraded ahead of the API); in that case rely on type and
message.Note: this endpoint always returns 200 for a known run — even one with status: "failed". The HTTP-level error path (RFC 7807) only applies when the call itself
fails (token invalid, run not found, etc.).
Returns 404 for both “run does not exist” and “run belongs to a different company” — the API does not distinguish them to avoid leaking existence.
agent_conversations resource with read verb.
{
"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": "completed",
"tool_choice": null,
"effective_config": { "...": "..." },
"iterations_used": 1,
"usage": { "prompt_tokens": 169, "completion_tokens": 8, "total_tokens": 177 },
"submitted_inference_job_ids": ["e94c6eab-019f-4e96-bb47-049fa60e7988"],
"pending_tool_calls": [],
"final_text": "4",
"error": null,
"started_at": "2026-05-18T13:21:30.355180Z",
"completed_at": "2026-05-18T13:21:51.983952Z"
}
{
"id": "19082f49-0280-4e32-a8b4-7df8dea9dbed",
"status": "requires_action",
"iterations_used": 1,
"pending_tool_calls": [{
"tool_use_id": "tooluse_DWXPKZ50JDGib5GmShyUgJ",
"name": "user-confirm_booking",
"arguments": { "proposed_slot": "tomorrow afternoon" }
}],
"final_text": null,
...
}
Resume by posting a new run with payload.kind: "tool_outputs" carrying the same
tool_use_id.
{
"id": "...",
"status": "failed",
"iterations_used": null,
"usage": null,
"final_text": null,
"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"
},
...
}
Documentation Index
Fetch the complete documentation index at: https://docs.narrative.io/llms.txt
Use this file to discover all available pages before exploring further.
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
The run's UUID.
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"
Run state. status indicates which lifecycle position the run is in; populated
fields vary accordingly (see description).
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 → final_text, iterations_used, usage, submitted_inference_job_ids.requires_action → pending_tool_calls, iterations_used, usage,
submitted_inference_job_ids.failed → error, possibly partial iterations_used/usage/submitted_inference_job_ids.pending / running → only the at-creation fields (id, conversation_id,
started_at, etc.) plus submitted_inference_job_ids if 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 against the same conversation returns the
original run row unchanged. This lets you retry network blips, request timeouts, etc.
without accidentally starting a second workflow.
Scope: (conversation_id, client_op_id) is the uniqueness key. You can reuse the
same client_op_id across different conversations; the platform doesn't deduplicate
cross-conversation.
"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. final_text is populated.requires_action — model called a client-side tool. pending_tool_calls is
populated; resume by posting a new run with payload.kind: tool_outputs.failed — non-recoverable error. error.type, error.message, error.title,
error.docs_url are populated. See the
error catalog
for the per-type meaning.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.
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.
"2026-05-18T13:21:30.355180Z"
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:
{ "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.
The model's final answer. The platform extracts it from the text field of the
model's structured output. Populated only on status: completed.
Populated only on status: failed.
When the run reached a terminal status. Null while in pending or running.