Skip to content

Session Files ​

Files uploaded to a session are read by the agent in the turns that follow. The files live in a Document Store bucket chosen by the agent's administrator; client.agents.sessions.files uploads, lists, downloads and deletes them.

New in 3.1.0

sessions.files.upload / list / delete, files.content / contentStream, Agent.attachments on agents.get(), and the bucket_key / path fields on every file. Files need a Console release that ships session files on buckets.

When to use ​

  • A user hands the agent a PDF, spreadsheet, image or text file and asks about it.
  • You want to upload first, ask later — the session exists before any message does.
  • You need to show or download what the agent was given, including the text it actually read.

The flow: create → upload → ask ​

typescript
import { readFile } from 'node:fs/promises';

const agent = await client.agents.get('support-bot');
if (!agent.attachments?.enabled) throw new Error('This agent takes no files');

const session = await client.agents.sessions.create({ agent: 'support-bot' });

const file = await client.agents.sessions.files.upload(session.id, await readFile('invoice.pdf'), {
  name: 'invoice.pdf',
  mimeType: 'application/pdf',
});
file.bucket_key; // "session-docs": the Document Store bucket it lives in
file.path;       // "agent-sessions/support-bot/<sessionId>/<unique name>": its path inside that bucket

// A file nobody has sent yet goes with the session's next message...
const reply = await client.agents.responses.create({
  model: 'support-bot',
  session_id: session.id,
  input: 'What is the total on this invoice?',
});

// ...or name it in the message.
await client.agents.responses.create({
  model: 'support-bot',
  session_id: session.id,
  input: [{ role: 'user', content: [
    { type: 'input_text', text: 'And the due date?' },
    { type: 'input_file', file_id: file.id },
  ] }],
});

A file for input_file / input_image is named with session_id, conversation or previous_response_id. A file nobody has sent yet goes with the session's next message anyway, unless the agent's Send new uploads with the next message setting is off.

Check what the agent accepts ​

agents.get(key).attachments (not in agents.list()) says whether and how much, before you upload anything:

FieldMeaning
enabledFiles are on for the agent and its bucket can take a file right now. When false, uploads are refused (403 attachments_disabled, or 409 storage_unavailable when the bucket cannot take a file at the moment).
max_filesFiles one session holds (0 while files are switched off)
max_file_size_mbLargest file in MB (0 while files are switched off)
allowed_mime_typesAccepted MIME types; image/* style wildcards allowed. Empty = any
accepts_imagesThe agent's model reads images. When false, an attached image is replaced by a note saying so; the model never sees it

When files are on but the bucket cannot take a file at the moment, enabled is false and the real limits are still reported. attachments is absent on a Console that predates session files.

Methods ​

CallWhat it doesRoute
files.upload(sessionId, bytes, { name, mimeType?, signal? })Upload (201): the raw bytes as the request body, not base64POST …/files?name=<name>
files.list(sessionId)The session's files, oldest firstGET …/files
files.content(sessionId, fileId, { variant?, signal? })Download the bytes: { data, contentType, fileName? }GET …/files/{fileId}/content
files.contentStream(sessionId, fileId, { variant?, signal? })The same as a stream ({ body, contentType, fileName?, size? }) for large filesGET …/files/{fileId}/content
files.delete(sessionId, fileId)Delete the file (object, converted text and record) → { id, object: 'agent.session.file', deleted: true }DELETE …/files/{fileId}

(… is /api/client/v1/agents/sessions/{sessionId}.)

Upload ​

typescript
const file = await client.agents.sessions.files.upload(session.id, bytes, {
  name: 'invoice.pdf',        // required for raw bytes; taken from a File when omitted
  mimeType: 'application/pdf', // taken from a Blob/File when omitted, else application/octet-stream
});
OptionDescription
bytesBlob, File, Uint8Array or ArrayBuffer
nameFile name. Required for raw bytes; read from a File. Without one the SDK throws CognipeerError before sending anything
mimeTypeMIME type. Read from a Blob / File when omitted, else application/octet-stream
signalAborts the request

JSON files are sent as application/octet-stream

The upload route also accepts a JSON { name, content_type, data: <base64> } body for small files, so it reads any JSON content type as that envelope. The SDK always sends the raw form, and therefore sends a JSON mimeType (application/json, *+json) as application/octet-stream. The file name still decides how a .json file is read.

The SessionFile object ​

typescript
interface SessionFile {
  id: string;                    // pass as file_id in an input_file / input_image part
  object: 'agent.session.file';
  session_id: string;
  name: string;
  mime_type: string;
  size: number;                  // bytes
  conversion_status: 'pending' | 'succeeded' | 'failed' | 'skipped' | (string & {});
  conversion_error?: string;     // why a conversion failed
  bucket_key: string;            // the Document Store bucket the file lives in
  path: string;                  // <folder>/<agentKey>/<sessionId>/<unique name>
  content_url: string;           // Console path that serves the bytes
  created_at?: string;           // ISO
  expires_at?: string;           // ISO — after this the Console deletes the file; absent = kept
}
conversion_statusMeaning
succeededThe agent can read the text (documents become markdown)
pendingStill converting
failedOnly the original exists (conversion_error says why)
skippedNeeds no conversion (an image)

bucket_key is the bucket the file was stored in when it was uploaded; changing the agent's bucket later affects new files only. content_url is a Console path (/api/client/v1/agents/sessions/{id}/files/{fileId}/content); prefer content() / contentStream(), which add the base URL and the token.

Download ​

typescript
import { writeFile } from 'node:fs/promises';

// The bytes as uploaded.
const { data, contentType, fileName } = await client.agents.sessions.files.content(session.id, file.id);
await writeFile(fileName ?? 'download', data);

// The text the agent reads (documents become markdown), when the conversion succeeded.
const { data: md } = await client.agents.sessions.files.content(session.id, file.id, { variant: 'markdown' });
console.log(new TextDecoder().decode(md));

// A large file: stream it instead of buffering.
const { body, size } = await client.agents.sessions.files.contentStream(session.id, file.id);

variant is original (default) or markdown. A file without a text version answers 409 markdown_unavailable (a picture, or a conversion that did not succeed — read the original instead). fileName is the base name from Content-Disposition. For contentStream() the client timeout covers the wait for the response headers only (a transfer that keeps producing bytes is not cut off); cancel body if you stop reading early.

The content call needs a recent Console

content() / contentStream() need a Console that serves the session-file content route (GET /agents/sessions/{id}/files/{fileId}/content). On an older one it answers 404.

Console settings that control files ​

Where files live, and the limits, are the agent's Configure → Files setting in the Console, not something a caller chooses. Defaults when a field is unset:

SettingDefaultEffect
Let people attach filesoffOff: uploads are refused (403 attachments_disabled). Needs a bucket
BucketnoneThe Document Store bucket the files are saved in. Only a bucket of the agent's project whose storage provider is active can be chosen
Folderagent-sessionsThe folder inside the bucket. Letters, digits and . _ - / only; no . or .. segments; at most 200 characters
Files per session10A further upload is 409 too_many_files
Largest file (MB)20Larger is 413 file_too_large
Keep files for (days)30After this the file is deleted from the bucket (expires_at); changing it affects files uploaded from then on
Allowed file typesanyMIME types; image/* and text/* match a whole family. Others are 415 unsupported_file_type
Put in the message up to (tokens)30,000The text of all files sent with one message is added up: under this it is put in the message; over it, none is, and the agent reads the files page by page with tools
Page size when reading (characters)20,000How much of a file the agent gets per read when it reads page by page
Send new uploads with the next messageonA file uploaded to a session that no message has carried yet goes along with the next message. Off: only files a message names are sent
Images per model request6Past this number, the oldest images become a note naming the file, which the agent can look at again
Image size per request (MB)12The most image data one model request carries, before encoding; the oldest images are left out first
Show recent images again (messages)2How many of the latest image-bearing messages are shown to the agent again when they fell out of its context; 0 = never

The agent reads session files with built-in read-only tools (session_files_list, session_files_read, and session_files_view_image while the session holds an image and the model takes images).

An image reaches the model only when the agent's model reads images (agent.attachments.accepts_images); otherwise the turn records a note that the image was not sent.

Files are ordinary bucket objects ​

The files are ordinary objects in the bucket, so the Files API sees them too. They carry purpose: 'conversation', conversationId, agentKey, expiresAt and uploadedBy:

typescript
const { files, folders } = await client.files.list(file.bucket_key, {
  prefix: 'agent-sessions/support-bot/',
  purpose: 'conversation', // only files attached to agent sessions; 'other' = only the rest
});

A bucket an agent keeps session files in cannot be deleted or disabled until the agent uses another one: the Files management API answers 409 bucket_in_use and lists the agents (error.response.agents). No call of this SDK raises it.

Limits ​

LimitValue
Files per sessionthe agent's setting (default 10; attachments.max_files)
File sizethe agent's setting (default 20 MB; attachments.max_file_size_mb), capped by the Console's overall upload ceiling
Retentionthe agent's setting (default 30 days)
Quotathe tenant's file quotas apply: 429 quota_exceeded

Errors ​

StatuserrorCodeWhen
403attachments_disabledThe agent takes no files: they are off for it, or on with no bucket chosen. Also raised by responses.create() with input_file / input_image parts
404session_not_foundNo such session in the token's project
404file_not_foundThe file is not on this session (or was deleted or expired)
409storage_unavailableThe agent's bucket cannot take a file now (deleted, disabled, or its provider is gone, inactive or not assigned to the project); the message names the bucket
409storage_not_configuredFiles are on but no bucket was chosen (older data); pick one in the agent's Files settings
409too_many_filesThe session already holds the agent's maximum
409markdown_unavailablevariant: 'markdown' on a file without a text version
413file_too_largeOver the agent's file size limit
415unsupported_file_typeThe type is not in the agent's allowed list
429quota_exceededRate or storage quota of the tenant

See Agent error codes for how to branch on them.

Security notes ​

  • Files are scoped to the token's project: a session — and so its files — of another project answers 404, even with its id.
  • A session file's bytes are served with Cache-Control: private, X-Content-Type-Options: nosniff and a sanitised Content-Disposition: pictures open in place, everything else downloads, and SVG and HTML always download. Do not re-serve a downloaded file under a content type you did not check.
  • Treat a file's name and content as untrusted input: the agent reads them, and a document can carry instructions aimed at the model.
  • Files expire. Do not use a session file as long-term storage; copy what you need to your own storage.

Troubleshooting ​

403 attachments_disabled. Files are off for the agent, or on with no bucket. Check agents.get(key).attachments.enabled and ask the agent's administrator to turn files on and pick a bucket in Configure → Files.

409 storage_unavailable. The bucket was deleted or disabled, or its storage provider is unusable; the error message names the bucket. An administrator fixes the bucket or picks another one.

The agent answers as if it never saw my file. Check file.conversion_status: pending means the text is not ready; failed means only the original exists. Then check the file reached a turn — name it with an input_file part, or make sure Send new uploads with the next message is on. For an image, check attachments.accepts_images.

markdown_unavailable on a PDF. The conversion did not succeed (conversion_status: 'failed'). Download the original with the default variant.

content() answers 404. Either the file is not on this session, or the Console is too old to serve the content route.

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub