Agents APIWorkspace

Outputs

Name the files you want from a session, and download them with links that keep working after it ends.

A session's files live on its own disk, which is deleted when the session ends. Outputs are the files you name when you create the session. LatentStack copies them out at the end of each run and keeps them after the session ends, with download links that need no API key. You can collect them later, or hand the links to someone else.

Name your outputs

Send a file in, name what comes out, and stop after the run
const session = await sessions.create({
  agent: { model: "anthropic/claude-sonnet-5.5" },
  input: "Sort the names in uploads/names.txt and save them as out/sorted.txt.",
  files: { "uploads/names.txt": "Mira\nArjun\nZoe\nLena\n" }, // in the workspace before the first message
  outputs: ["out/*.txt"], // copied out when the run ends, and kept after the session
  end_after_run: true, // stop once the run has ended
})
outputslist of pathsoptional
Up to 50 workspace paths, such as out/report.pdf. In a name, * matches any characters and ? one character, within one folder: out/*.json is every JSON file directly in out/, not in the folders below it. reports/*/summary.md works too. ** isn't supported.
filesobjectoptional
Files to put in the workspace before the first message runs: path to content. See Send files with create.
end_after_runbooleanoptionaldefault false
Stop the session once its run has ended. See End after the run.

Paths follow the same rules as file calls: relative to the workspace, no .., and nothing inside .git. The agent is already told to leave what you ask for in out/, so naming the file in your message is enough.

When they're copied

  • At the end of every run, whether it completed or failed. The copy is made before the run's webhook is sent, so run.completed already lists the files with their links.
  • Once more when the session stops while its sandbox is still up: when you delete it, or after its run with end_after_run. A session stopped while paused has no sandbox to copy from, so the copies made before are what you get.
  • When it pauses with its sandbox up, including at a spend limit, where its run never reaches an end. The session.paused webhook then lists the files with their links.
  • A file that hasn't changed since its last copy (the same size and modification time) isn't copied again.
  • Each path keeps only its newest copy.

When something is missing

The run's run.completed or run.failed webhook says what this copy couldn't do:

FieldMeaning
outputs_missingPatterns that matched no file this time.
outputs_errorsFiles or patterns that couldn't be copied, each with a message.
outputs_truncatedA pattern looks through at most 1,000 files under its folder, subfolders included. true when a folder held more.
outputs_errorThe copy failed as a whole. The outputs already kept are still listed.

List and download

Wait for the session to stop, then download its outputs
await sessions.wait(session.id, { until: ["stopped", "failed"] })

const listing = await sessions.outputs(session.id)
for (const output of listing.data) {
  // The link needs no API key and works for 24 hours.
  const response = await fetch(output.url)
  await writeFile(basename(output.path), Buffer.from(await response.arrayBuffer()))
  console.log(output.path, output.size, "bytes")
}

A listing is { object: "list", data, patterns }: the outputs in path order, and the patterns the session asked for.

JSON
{
  "id": "out_5e0a91c2d4b7e8f1a2c3d4e5",
  "object": "agent.output",
  "path": "out/sorted.txt",
  "size": 20,
  "sha256": "9f2c…",
  "content_type": "text/plain",
  "run_seq": 14,
  "captured_at": "2026-10-14T06:12:40.118000+00:00",
  "url": "https://latentstack.dev/v1/agents/outputs/out_5e0a91c2d4b7e8f1a2c3d4e5/content?token=…",
  "url_expires_at": "2026-10-15T06:13:02.540000+00:00"
}
FieldMeaning
path · size · sha256 · content_typeThe file, as it was copied.
run_seqThe event number of the run end (or the stop) that copied it.
captured_atWhen it was copied.
url · url_expires_atA download link, and when it stops working.
  • A link downloads the file without an API key until url_expires_at: 24 hours after you list. List again for fresh links.
  • Links inside a run.completed or run.failed webhook last 4 days, longer than the webhook's retries.
  • A link always gives the newest copy of its path.
  • A browser saves the file rather than opening it.
  • An expired link, or one for a deleted output, answers 404.
Warning
Anyone with the link can download the file
The link is the permission. Treat it like the file itself: don't log it, and share it only with people who may see the file.

Keep and delete

Outputs stay after the session stops or is deleted, until you delete them. List them with the session's id as before. The rest of the session's disk is still deleted when the session ends.

Delete an output
await sessions.deleteOutput(session.id, output.id)

Its links stop working at once. If the session runs again, the next run's end copies the file again under a new id.

Send files with create

files on create puts files in the workspace before the first message runs, so input can use them at once. You don't have to wait for the sandbox and call uploadFile().

  • It maps a workspace path to the file's content. In both SDKs a string is the file's text, stored as UTF-8, and bytes (Uint8Array in TypeScript) are its content. The SDK sends them as base64; over HTTP, send base64 yourself.
  • The files travel inside the create request, which is at most 50 MB in all (request_too_large otherwise). For larger files, create the session without input, upload them with uploadFile() once it is running (any size), then send the message.
  • If a file can't be put in place, the session fails with an error of type files_error that names the file.

End after the run

With end_after_run: true, the session stops as soon as a run ends. First its patch is saved, its outputs are copied and the run's webhook is sent. Then it stops with stop_reason run_finished.

  • Use it for one-off jobs, such as CI or a webhook that starts a task. Waiting for stopped or failed, as the example does, then tells you the work is done.
  • Such a session has nothing to come back to, so where another session would pause, it stops: when idle (stop_reason idle_timeout) or when its sandbox reaches its time limit (hard_limit). If it pauses anyway, at a spend limit for example, it waits 7 days for you to raise the limit and continue, then stops. See Pause and resume.
  • Schedules turn it on for their sessions unless you say otherwise.

Over HTTP

RequestDoes
GET /v1/agents/sessions/{id}/outputsLists the outputs, each with a fresh link.
DELETE /v1/agents/sessions/{id}/outputs/{output_id}Deletes one. Answers 204.
GET <url>Downloads the file. No API key: the token in the link is the permission.

On POST /v1/agents/sessions, files is an object of path to base64 content, outputs a list of paths, and end_after_run a boolean. The session carries its outputs and end_after_run back.

Note
Turned on per deployment
Your administrator turns outputs on for a deployment. Where they're off, outputs on create is refused with invalid_request and the outputs requests answer 404.