Overview
The agent surface is a small, stateful layer on top of the rest of the v2 API: a session is a conversation, and each question you ask it is a turn. Every turn runs asynchronously, the same way as every other v2 operation — you get back ajob_id immediately and poll GET /v2/jobs/{job_id} for the
answer.
Examples use the default base URL,
https://api.orion.file.ai/prod/v1. If
your workspace is on an instance-specific host, swap the hostname and change
nothing else — see Switching between
instances.Create a session
POST /v2/agent/sessionsList sessions
GET /v2/agent/sessionsGet a session
GET /v2/agent/sessions/{session_id}Ask a question
POST /v2/agent/sessions/{session_id}/askStop the running turn
POST /v2/agent/sessions/{session_id}/stopRead the conversation
GET /v2/agent/sessions/{session_id}/messagesDelete a session
DELETE /v2/agent/sessions/{session_id}Poll a job
GET /v2/jobs/{job_id}Authentication
Every route on this page uses the samex-api-key header as the rest of the
API — see About the fileAI API.
Idempotency
Three of these routes are write operations that carry a requiredIdempotency-Key header, not merely an optional one:
The header being required here (rather than optional, as on most of the API)
is deliberate: a session opened twice because a
POST /v2/agent/sessions
retry timed out, or a turn asked twice because an ask retry raced the
original, are both mistakes an idempotency key exists to prevent. See
Idempotent requests for the general replay
mechanics — the 24h window, the Idempotent-Replayed header, and what counts
as “the same body” all apply here unchanged.
ask and stop are also serialized by a separate rule that has nothing to
do with idempotency: a session runs one turn at a time. Asking again
while a run is live is a 409 AGENT_RUN_IN_PROGRESS, independent of whether
you sent a fresh Idempotency-Key.The async job lifecycle
Every turn follows the same three steps as the rest of the v2 API’s async operations:1
ask
POST /v2/agent/sessions/{session_id}/ask adds the turn to the session and
returns 202 Accepted immediately — never an answer.2
poll job_id
Poll the returned
poll_url — GET /v2/jobs/{job_id} — until status
reaches a settled value.3
or receive the callback
If the API key has a callback URL configured, the settled job is also
POSTed there — you don’t have to poll if you’d rather be notified. Poll
poll_url as a fallback regardless: a webhook delivery can be delayed or
lost, while the job itself is durable.GET /v2/jobs/{job_id} is shared with other v2 operations. Branch on kind
before reading the rest of the body — an agent job (kind: "agent", id
prefixed sajob_) reports the statuses above and carries result.answer;
other kinds report their own statuses and results.
Listing and reading sessions
GET /v2/agent/sessions is cursor-paginated like the rest of the API’s list
endpoints — see Pagination for the general cursor
mechanics. Rows are newest first and deliberately omit run_status:
resolving it costs a query per session, so read a single session
(GET /v2/agent/sessions/{session_id}) when you actually need to know
whether it can take a turn.
run_status is how you tell whether the session is free:
A run whose worker died sends no terminal event, so a run that stopped
reporting is only reconciled to
STALE when you read the session (or
its messages) — reading is what frees a session stuck behind a dead run.Reading the conversation
GET /v2/agent/sessions/{session_id}/messages returns turns oldest first,
one row per turn, cursor-paginated with a smaller default page (20, vs. 50
elsewhere) because a turn carries a whole answer.
An answer longer than 256KB is returned truncated, with content_truncated: true and content_length giving its real size — fetch the turn’s job_id
via GET /v2/jobs/{job_id} to read it in full.
Stopping a run
POST /v2/agent/sessions/{session_id}/stop asks the agent to end the live
run and returns immediately with stop_requested. It is a request, not a
completion:
- The agent keeps working for about a second while it checkpoints its partial work, then ends the run itself — the answer produced so far is kept, and the time and cost already incurred are still billed.
- A stop always races the run it is ending: if the run had already finished,
the response reports
stop_requested: falseand nothing was forwarded. This is a normal outcome, not an error. - The session stays open — the next turn continues from the checkpoint the agent wrote.
job_id to see the run reach STOPPED.
Deleting a session
DELETE /v2/agent/sessions/{session_id} deletes the session, stopping a live
run first rather than refusing the delete. stopped_run in the response
tells you whether that happened. Nothing is lost: the agent still ends the
run properly, and its job lives outside the session — the partial answer,
the real cost, and any callback all still arrive via GET /v2/jobs/{job_id}. The turns themselves are retained but become unreachable;
every route on a deleted session, including DELETE again, returns 404.