> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bonya.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Transfer files

> Upload, download, list, and inspect files on a sandbox

Every sandbox exposes a filesystem API under `/v1/sandbox/{sandbox_id}/fs`. Read and write bodies are **raw bytes** — not tar archives, not multipart form data — so each call moves exactly one file. See [Files](https://docs.bonya.ai/concepts/files.md) for the full field reference; this guide is workflow-focused.

## Upload a file

```shell cURL theme={null}
curl -s -X PUT "https://api.bonya.ai/v1/sandbox/$SANDBOX_ID/fs/write?path=/workspace/hello.txt" \
  -H "Authorization: Bearer $BONYA_API_KEY" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @./hello.txt
```

```shell CLI theme={null}
bonya fs upload $SANDBOX_ID ./hello.txt /workspace/hello.txt
```

```ts TypeScript theme={null}
await sandbox.uploadFile("./hello.txt", "/workspace/hello.txt");
```

```python Python theme={null}
sandbox.upload_file("./hello.txt", "/workspace/hello.txt")
```

```go Go theme={null}
sandbox.UploadFile(ctx, "./hello.txt", "/workspace/hello.txt")
```

`fs/write` returns `204` with no body on success. The SDKs stream in 64 KiB chunks through a guest-side temp file and publish atomically, so a reader never observes a partially written file.

## Download a file

```shell cURL theme={null}
curl -s "https://api.bonya.ai/v1/sandbox/$SANDBOX_ID/fs/read?path=/workspace/hello.txt" \
  -H "Authorization: Bearer $BONYA_API_KEY" \
  -o ./hello.downloaded.txt
```

```shell CLI theme={null}
bonya fs download $SANDBOX_ID /workspace/hello.txt ./hello.downloaded.txt
```

```ts TypeScript theme={null}
await sandbox.downloadFile("/workspace/hello.txt", "./hello.downloaded.txt");
```

```python Python theme={null}
sandbox.download_file("/workspace/hello.txt", "./hello.downloaded.txt")
```

```go Go theme={null}
sandbox.DownloadFile(ctx, "/workspace/hello.txt", "./hello.downloaded.txt")
```

`fs/read` returns the raw bytes as `application/octet-stream` — there's no envelope to unwrap.

> [!NOTE]
>
> There's no directory/tar transfer mode in this API. `upload`/`download` (CLI and SDKs) and `fs/read`/`fs/write` (REST) each move exactly one file. To move a directory tree, upload or download its files individually, or run a command via [exec](https://docs.bonya.ai/guides/run-a-command.md) that packs/unpacks an archive on the sandbox side.

## Write small content directly

For short text content you don't want to stage as a local file first, `read`/`write` on the SDK take bytes or a string directly rather than a file path:

```ts TypeScript theme={null}
await sandbox.writeFile("/workspace/message.txt", "hello\n");
const payload = await sandbox.readFile("/workspace/message.txt");
```

```python Python theme={null}
sandbox.write_file("/workspace/message.txt", "hello\n")
payload = sandbox.read_file("/workspace/message.txt")
```

## List a directory

```shell cURL theme={null}
curl -s "https://api.bonya.ai/v1/sandbox/$SANDBOX_ID/fs/list?path=/workspace" \
  -H "Authorization: Bearer $BONYA_API_KEY"
```

```shell CLI theme={null}
bonya fs ls $SANDBOX_ID /workspace
```

```ts TypeScript theme={null}
const entries = await sandbox.listFiles("/workspace");
```

```python Python theme={null}
entries = sandbox.list_files("/workspace")
```

The response is `{ "files": [ ...FsFileInfo ] }` — immediate children only, sorted by name, each carrying `path`, `name`, `kind` (`FILE`/`DIRECTORY`/`SYMLINK`/`OTHER`), `size`, `mode`, and `modified_at_unix_nanos`.

## Stat a file

```shell cURL theme={null}
curl -s "https://api.bonya.ai/v1/sandbox/$SANDBOX_ID/fs/stat?path=/workspace/hello.txt" \
  -H "Authorization: Bearer $BONYA_API_KEY"
```

```shell CLI theme={null}
bonya fs stat $SANDBOX_ID /workspace/hello.txt
```

```ts TypeScript theme={null}
const info = await sandbox.statFile("/workspace/hello.txt");
```

```python Python theme={null}
info = sandbox.stat_file("/workspace/hello.txt")
```

`stat` returns lstat-style metadata — a symlink is reported as a symlink, not followed.

## Directories, renaming, and deleting

```shell cURL theme={null}
# mkdir
curl -s -X POST "https://api.bonya.ai/v1/sandbox/$SANDBOX_ID/fs/mkdir" \
  -H "Authorization: Bearer $BONYA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"path": "/workspace/output"}'

# rename
curl -s -X POST "https://api.bonya.ai/v1/sandbox/$SANDBOX_ID/fs/rename" \
  -H "Authorization: Bearer $BONYA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from": "/workspace/hello.txt", "to": "/workspace/output/hello.txt"}'

# delete (recursive)
curl -s -X DELETE "https://api.bonya.ai/v1/sandbox/$SANDBOX_ID/fs?path=/workspace/output&recursive=true" \
  -H "Authorization: Bearer $BONYA_API_KEY"
```

```shell CLI theme={null}
bonya fs mkdir $SANDBOX_ID /workspace/output
bonya fs mv $SANDBOX_ID /workspace/hello.txt /workspace/output/hello.txt
bonya fs rm $SANDBOX_ID /workspace/output --recursive
```

```ts TypeScript theme={null}
await sandbox.mkdirFile("/workspace/output");
await sandbox.moveFile("/workspace/hello.txt", "/workspace/output/hello.txt");
await sandbox.removeFile("/workspace/output", true);
```

```python Python theme={null}
sandbox.mkdir_file("/workspace/output")
sandbox.move_file("/workspace/hello.txt", "/workspace/output/hello.txt")
sandbox.remove_file("/workspace/output", recursive=True)
```

`move`/`rename` is same-filesystem and atomic — it doesn't overwrite an existing destination, and won't move across filesystems. `remove` with `recursive` set removes a non-empty directory without following symlinks.

## Related

> [card] [Files](https://docs.bonya.ai/concepts/files.md)

> [card] [Sandboxes](https://docs.bonya.ai/concepts/sandboxes.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.