> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://platform.bctrl.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://platform.bctrl.ai/_mcp/server.

# Files

> Durable file storage scoped to a space - upload, list, browse by folder, and download.

`bctrl.files` is the durable storage namespace a [space](/sdk/spaces) exposes. Files you upload and files a runtime produces both live here, survive past any single run, and are readable by every runtime in the space.

> Three file surfaces, three jobs: `bctrl.files` is the durable space store (this page). [`bctrl.runs.files`](/sdk/run-files) is the read-and-export view of what one run produced. `bctrl.runtimes.files` stages files into - and collects them out of - a *live* runtime. They all point at the same underlying storage.

## Upload a file

```ts
const file = await bctrl.files.upload({
  file: new Blob(["name,email\nada@acme.com"]),
  name: "contacts.csv",
  path: "imports/2026/contacts.csv", // optional storage path
});

console.log(file.id, file.path, file.downloadUrl);
```

The `file` may be any `Blob`. Pass `spaceId` to target a space other than the API key's default. Files default to `source: "upload"`; files a runtime writes back arrive as `source: "runtime"`.

## List files

```ts
const { data } = await bctrl.files.list({ type: "download" });

for await (const file of bctrl.files.iter()) {
  console.log(file.name, file.sizeBytes, file.contentType);
}
```

`list` returns one page with a cursor; `iter` walks every page. Narrow the set with `prefix` (a path prefix), `source` (`"upload"` or `"runtime"`), `type`, `runId`, `runtimeId`, `q` (name search), or `createdAfter`. See [Pagination](/sdk/essentials).

## Browse by folder

By default `list` returns a flat file list. Pass `include: "folders"` for an S3-style directory view: `data` holds only the files directly under `prefix`, and the response gains a `folders` array of immediate-subfolder rollups - so you can render a file tree without scanning every key.

```ts
const { data, folders } = await bctrl.files.list({
  prefix: "imports/2026/",
  include: "folders",
});

for (const folder of folders ?? []) {
  // name, path, fileCount, totalBytes, lastCreatedAt
  console.log(`${folder.name}/  (${folder.fileCount} files, ${folder.totalBytes} bytes)`);
}

for (const file of data) {
  console.log(file.path); // files directly under imports/2026/
}
```

Each folder rollup carries `fileCount`, `totalBytes`, and `lastCreatedAt` (the newest file under it, or `null` when empty), so a listing screen has its totals without a second pass.

## Read, update, download, delete

```ts
const file = await bctrl.files.get(file.id);

// Fetch the bytes - either follow the signed URL...
const res = await fetch(file.downloadUrl);
// ...or stream them through the client.
const stream = await bctrl.files.content(file.id);

// Rename or re-tag without re-uploading.
await bctrl.files.update(file.id, {
  name: "contacts-deduped.csv",
  metadata: { reviewed: "true" },
});

await bctrl.files.delete(file.id);
```

## Next

* [Spaces](/sdk/spaces) - the storage namespace files belong to
* [Run Files](/sdk/run-files) - list and export what a single run produced
* [Runtimes](/sdk/runtimes) - stage files into and collect them from a live runtime