Files

View as Markdown

bctrl.files is the durable storage namespace a space 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 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

1const file = await bctrl.files.upload({
2 file: new Blob(["name,email\n[email protected]"]),
3 name: "contacts.csv",
4 path: "imports/2026/contacts.csv", // optional storage path
5});
6
7console.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

1const { data } = await bctrl.files.list({ type: "download" });
2
3for await (const file of bctrl.files.iter()) {
4 console.log(file.name, file.sizeBytes, file.contentType);
5}

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.

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.

1const { data, folders } = await bctrl.files.list({
2 prefix: "imports/2026/",
3 include: "folders",
4});
5
6for (const folder of folders ?? []) {
7 // name, path, fileCount, totalBytes, lastCreatedAt
8 console.log(`${folder.name}/ (${folder.fileCount} files, ${folder.totalBytes} bytes)`);
9}
10
11for (const file of data) {
12 console.log(file.path); // files directly under imports/2026/
13}

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

1const file = await bctrl.files.get(file.id);
2
3// Fetch the bytes - either follow the signed URL...
4const res = await fetch(file.downloadUrl);
5// ...or stream them through the client.
6const stream = await bctrl.files.content(file.id);
7
8// Rename or re-tag without re-uploading.
9await bctrl.files.update(file.id, {
10 name: "contacts-deduped.csv",
11 metadata: { reviewed: "true" },
12});
13
14await bctrl.files.delete(file.id);

Next

  • Spaces - the storage namespace files belong to
  • Run Files - list and export what a single run produced
  • Runtimes - stage files into and collect them from a live runtime