Skip to main content
Files is Vetta’s object storage. Use it to upload inputs an agent can read and to hold artifacts an agent publishes. Files are either session-scoped (temporary, tied to a session) or published/persistent (retained at the organization level).

The file object

string
Unique id (e.g. fil_01H...).
string
Always file.
string
File name.
string
MIME type.
integer
Size in bytes.
string
session (temporary) or published (persistent).
string | null
Owning session, for session-scoped files.
string
Content hash.
string
Upload timestamp.

Session-scoped vs published

Anything an agent should keep beyond a run is promoted to a published file. Scratch work stays session-scoped. Promotion emits no event — the event stream carries session activity only, so read the file back if you need to confirm it.

Getting bytes out of a session

Files travel outward: an agent writes an artifact with the built-in publish_file tool and you read it back through this API. There is no create-time mount — a session takes no files[] — and there is no read_file tool; the built-in toolset is bash, read, write, edit, ls, find, browser, read_skill, publish_file, web_search, web_fetch, generate_image and generate_video, and the file tools address the sandbox filesystem, not file ids. To put an input in front of an agent today, put it in the session’s first message or have the agent fetch it. Each publish_file call mints a new file id, including for a name that already exists: nothing is superseded and both remain listed. Pick the one you want by created_at.

Upload a file

POST /v1/files201 Created. Accepts a multipart upload. The file part carries the bytes; other parts set metadata.
file
required
The file contents (multipart).
string
session or published. Defaults to published.
string
Required when scope is session.
string
Override the file name.

Retrieve a file

GET /v1/files/{id}200 OK with file metadata. Append ?download=true to receive the raw bytes instead of JSON.
Response
POST /v1/files/{id}/url201 Created. Scope files:read. A file id is not something a person can watch. ?download=true above wants an authorization header, and an <img> or <video> tag sends none — so this mints an address that carries its own proof instead, good for seven days.
Response
GET /v1/files/{id}/{name}?org=&expires=&sig= — that URL, with no credential at all — returns the bytes, inline, with the content type they were stored under. The filename is in the path so the address ends in a real extension, which is what lets a browser tag and a fetch-once publishing provider recognise it before opening it. The signature covers every other field in the URL, so there is nothing in it you can edit: change the expiry, the file, the organization or the name and it answers 403. A forged link and an expired one answer the same thing. Deleting the file revokes every link ever minted for it.

List files

GET /v1/files200 OK, cursor-paginated. Filter with ?scope= and ?session_id=. See Pagination.
Response

Publish a file

POST /v1/files/{id}/publish200 OK. Scope files:write. The promotion boundary: a session-scoped file becomes a durable, org-level published one. Use it when work that started as scratch turns out to be the deliverable — it saves re-uploading the bytes. Idempotent. Publishing an already-published file returns it unchanged rather than erroring, so a retry is safe. No request body.
Response
The file now outlives its session and is billed as org-level storage. No event is emitted. Errorsnot_found (404); insufficient_scope (403) without files:write.

Delete a file

DELETE /v1/files/{id}200 OK. Removes the object.
Response