Skip to main content
Everything POST /v1/sessions accepts, and what each field is for. The quickstart creates a session with a model and nothing else, which is the right way to see the thing work. This page is for the second session — the one that needs your repository, your files, your secrets, or a limit on what the agent may do without asking. Nothing here is required. A session with only a model is a complete session.

The fields

POST /v1/sessions takes more than a model. Every field below is optional.
agent.instructions, agent.approval_mode, agent.permission_rules and metadata can also be changed later with PATCH /v1/sessions/{id}, which takes any combination of those and title.

Reading a session back

Everything you can set, you can read. GET /v1/sessions/{id} reports the model, the credential, the approval mode, the permission rules, the instructions — and the tools, with any secrets removed:
On the single read only. A page of sessions does not carry it: reading one session’s tools is a second lookup, and twenty of them to answer a question about one is not a trade worth making. That matters after a restart. Before answering a required action you can check the session is configured with the function you are about to answer for, rather than assuming it.

Repository

Two things about this are worth knowing before you rely on it. A 201 does not mean the code is there. The session is created immediately; the clone happens when its workspace comes up, which is later. If it fails, the session still starts with an empty workspace and the reason is on the session:
Read environment.repo.clone_error before concluding the agent ignored your instructions. The branch is reported, not chosen. The clone checks out the repository’s default branch and tells us which one that was; environment.repo.branch is that answer. Sending environment.branch is refused rather than ignored, because storing a value that changes nothing is worse than saying no. Your organization needs a GitHub connection — Console, Settings → App integrations. Without one, a request naming a repository is refused up front rather than producing a session that can never clone.

Files at creation

Not every job is “work on my repo”. When what you have is a CSV, a spec or a PDF, send the bytes:
data is base64 of the file’s bytes. path may be absolute under /workspace or relative to it — notes.md above lands at /workspace/notes.md. A path that resolves outside the workspace is refused, and the refusal tells you where it resolved to. Ceilings are in limits.md: 5 MiB a file, 10 MiB a request, 50 files. Each applies to the decoded bytes, not the base64. A file that could not be written says so. The session still opens — one unwritable path must not cost you the other forty-nine — and an error item names the file and the reason. See troubleshooting.md. A 201 does not mean the files are there — same as the clone. They are written when the workspace comes up, after the clone, so you can drop a config file into a repository you also asked for. They are written only if not already present. A sandbox that was paused and woken keeps whatever the agent did to those files; one that had to be rebuilt gets them again, because they are part of how the session was defined. There is no type: "file_id" and no type: "url". Gobare has no file store to reference, and does not fetch addresses on your behalf. For a large input, use environment.repo.

Files afterwards

The same shape, against a session that is already running:
The answer is what was written, not an acknowledgement — your next move is usually to tell the agent to read it, and you need to know that is safe. Two differences from environment.files: It overwrites. A path that is already there is replaced, because that is what you asked for. Seeded files are the opposite: they never overwrite.

Forking

A new session that starts where the original is, not where it began: the workspace and the transcript come with it. That is the point — trying two approaches from a state worth keeping, without paying for the setup twice or losing the first attempt. The original is untouched. It spends a session slot, so it is refused by the same ceiling with the same project_limit_exceeded as creating one. workspace_copied is worth reading. A fork whose workspace could not be copied is still a useful session, and believing the files are there when they are not means finding out from the agent — the worst place to find out. A session mid-turn, or with queued messages, is refused with conflict: fork it when the work in flight has finished.

Reading it back

A read is of the last snapshot, not the live sandbox. captured_at says which moment, and state: "missing" means none has been taken yet — which is not the same as an empty workspace. POST /files/refresh takes a fresh one; it is a separate call, and needs sessions:write, because it wakes a paused sandbox and that costs money and seconds. Environment files and key material are withheld, and say so — permission_denied rather than a 404 that would read as “not there”. Directories and withheld paths are marked readable: false in the listing. This is what answers “the agent said it wrote that, did it?” — a question the artifact list cannot, because artifacts only ever cover /workspace/outputs. It is not remembered. A live write is working state, not part of how the session was defined, so a sandbox rebuilt from nothing will not have it. A paused sandbox keeps it — and a paused session is woken to serve this call rather than refusing it.

Environment profiles

Environment profiles are named groups of variables, managed in the Console. Bind them by id:
Values are never returned by this API, and there is no endpoint to set them. variable_count is there so you can recognise the profile you meant. Omitting profiles inherits the organization’s default group; sending an empty list binds nothing. A profile belonging to another organization answers not_found rather than a permission error, so an id cannot be probed for existence.

Instructions

These sit on top of the product’s own rules rather than replacing them, and they apply to every turn. Where your instructions and our safety rules disagree, ours win — so instructions is how you shape an agent’s behaviour, and approval_mode and permission_rules below are how you constrain what it may actually do. Two different jobs; instructions are not a permission system. Two things worth knowing before you rely on them:
  • They take effect on the session’s next workspace, not mid-turn. The system prompt is fixed when the agent’s session is built. A PATCH during a running turn is not ignored — it applies from the next one.
  • A session older than this feature refuses them. Setting instructions on a workspace whose runtime predates them answers bridge_incompatible rather than accepting the field and running without it. Deleting the session and creating a new one gets you a current workspace.
Send null or "" to clear them.

Approval mode and permission rules

read_only and MCP

A tool this session cannot vouch for is a tool it will not run. MCP servers declare what a tool does with readOnlyHint in tools/list; read_only allows the ones that carry it and refuses the ones that do not, because “no annotation” is not the same as “harmless” — and an organization’s private plugin has been reviewed by nobody. This bites the obvious shape: a read-only investigation with a read-only runbook server. If the server does not annotate, either have it do so, or say so yourself:
A rule beats the mode, in both directions, which is what makes this expressible. A refusal is visible. It arrives as approval.resolved on the event stream with approved: false, the tool’s name, a code of denied_read_only or denied_by_rule, and the same sentence the agent was given. Without that, an agent saying “I could not reach the runbook” is indistinguishable from a model that did not try — which is what it looked like before this event carried a name. permission_rules narrows further, and applies in every mode:
Both are readable back on the session, so what a session enforces is never something you have to remember having sent.

Opening message

Pass input and the session starts working immediately:
It is all or nothing. A 201 means the session exists and the message was accepted. If the message cannot be sent, the session is destroyed and you get the send’s error — so a non-2xx means nothing exists and there is nothing to clean up. Unless we say otherwise, in the error itself. Destroying the session is the one step that can also fail. When it does, the message tells you so and names the session, because the alternative is a slot held by something you were never given the id of:
input takes the same shapes as input.message content: a plain string, or the content-part array. The steps below then apply unchanged; skip step 4.

Next

  • input — sending the session work once it exists
  • preview — publish what it builds
  • tools — give it your own functions