Create Agent Run
Authentication
Enter your API key with the Bearer prefix, e.g. ‘Bearer sk_…’.
Path parameters
Agent id (prefixed external id, agent_...).
Headers
Request
The task or goal to give the agent. The agent runs its brain against this over a short internal conversation and returns its result. The server’s limit is 8000 bytes, so a mostly non-ASCII instruction reaches it before 8000 characters.
The files to hand the agent, as file_... ids from
POST /v1/files. The
agent is told what it holds and reads one with its read_file
tool: PDF, HTML, Markdown and plain text are extracted, and an
image is transcribed and described by a vision model. At most 10.
Each id is checked at admission against this workspace and
against user_identity: a file uploaded for one person is not
readable by a run acting for another. An id that does not resolve
fails the request with a 400 naming attachments - the run is
never started over a file it cannot read.
Per-run values that seed the agent’s flow variables (override its
stored defaults). The agent’s prompt renders against the result
before every step: a declared variable the run does not supply
takes its default, one the run supplies takes the run’s value, and
a placeholder nothing supplies renders empty. The reserved
system__caller_id, system__agent_id, system__language and
system__memory keys are bound by the platform. The system__* namespace and the legacy memory
alias belong to the platform and are rejected with a 400 naming
variables, the same rule a conversation applies: the run binds its
own values there, including system__caller_id for the person it
acts for.
Upper bound on the run’s internal turn budget - one turn is one plan-act-observe cycle, so a run that calls three tools uses at least four. Defaults to 8 when omitted.
Clamped to the workspace’s per-run ceiling (5 on Free, 10
on Starter, 20 on Pro, 30 on Scale, 50 on Enterprise; per-workspace
overrides apply): the run’s input.max_turns echoes the budget it
actually got, and GET /v1/workspaces/current/entitlements
(max_run_turns) reports the ceiling up front, so plan against
that rather than the value you sent. An omitted max_turns takes
the default, clamped to the ceiling. On Free the
ceiling is below the default, so omitting this field there yields
5, not 8.
A run that exhausts its budget settles succeeded with
incomplete_reason: max_turns_exhausted and whatever answer it had
reached. Schema repairs count against this budget too - see
output_schema.
The person this run acts for, in your own vocabulary - the same field a conversation and a widget session take, so one workspace never has two answers to who a person is. The agent opens the run knowing what it has already learned about them, and what a run that succeeds learns from its instruction and its reply is written back under this value, exactly as a call writes memory.
Omit it to run the agent for nobody in particular, which is how a
run behaves with no memory of anyone and learns nothing. Must not begin with user_,
embed_ or anon_, which name identities the platform derives.
Every tool the run calls is told this value: a webhook receives
it as user_identity inside the signed body, an MCP server as
the Speechify-User-Identity header, and it renders in a tool’s
templated URL or headers as {{system__caller_id}}. A connector
you wrote can therefore look up that person’s own third-party
token, which is how you integrate a system Speechify holds no
credentials for.
Optional JSON Schema (2020-12) the run’s final answer must satisfy.
When set, the agent answers with a JSON object, the platform
validates it, and the conforming object is returned as
output.data.
The top level must be type: object - an array-typed or scalar
schema is refused at create with 400. At most 16 KiB.
On a mismatch the platform re-asks the agent, feeding back up to 8
of the violations. At most two repair attempts, and each one
spends a turn from max_turns - so a schema-constrained run on a
5-turn ceiling has little room left for tool calls. Each attempt is
journaled as an observation step whose tool is the reserved
name output_schema, which a timeline renderer should expect
alongside real tool names.
A run that never produces a conforming object settles succeeded
with incomplete_reason: output_schema_violation and no
output.data - its prose answer is still on output.reply. The
platform never returns an object the schema refused.
Up to 16 arbitrary key/value pairs echoed back on the run. Your own correlation ids belong here - the platform never reads them.
Response headers
Response
Run id (prefixed external id, arun_...).
The files this run’s own tool calls produced, in the order they were
made: a picture from generate_image or edit_image, a chart from
render_chart, a file run_code kept, a tool response that was not
text. Each is the same object GET /v1/files/{file_id} returns, so
content_path streams the bytes and source.step is the journal
step of the tool call that made it, with no second lookup.
Always present. It grows while the run is going - the event stream
carries each file on the step that produced it - and an empty list on
a finished run means it produced nothing, never that the files are on
another page. A run that failed or was cancelled after making a file
still lists it. A file scoped to the run’s user_identity is listed
here and is readable by a run acting for that same person, exactly as
under /v1/files. A file deleted or expired since is no longer listed.
Lifecycle: queued -> running -> succeeded | failed | canceled | expired. requires_action (a pending human approval) and canceling are transient. Terminal set: succeeded, failed, canceled, expired.
The project this run belongs to; null when it belongs to none. Captured when the run was created, so it does not move if the agent later does.
The run’s result, present only when status is succeeded.
Present only when status is failed.
Why a non-failed run stopped short: max_turns_exhausted, budget_exhausted, or output_schema_violation (the agent never produced an object matching input.output_schema; output.reply keeps its prose and output.data is absent).
What the run spent, present only once it has settled: wall-clock, the tokens summed across every step, the same tokens split per model, and what they cost at your plan’s rates. Written once at settle and never restated; the webhook payload carries the same values. input_tokens includes the cached reads and cached_input_tokens is that subset, so the two are never added. cost_micro_usd is an integer in millionths of a US dollar, computed from the rate card version named alongside it, and is informational: your invoice is authoritative. A delegated child run reports its own usage; the parent never sums it.
A human approval a run is durably parked on (present on AgentRun only while status is requires_action). Rendered VERBATIM for the approver - never a summary the agent wrote - so an injected agent cannot misrepresent what it is about to do. Resolve it with submitRun.
The run that delegated this one a sub-goal, absent on a root run. It is the upward half of lineage - what makes a child run of a member agent attributable to the team run that caused it (listRunChildren is the downward half).
When a worker picked the run up. Absent while queued; the gap between this and created_at is queue wait, not agent time.
When the run settled. Present for every terminal status - succeeded, failed, canceled and expired alike - and absent otherwise.