#Volume HTTP
Use the Volume HTTP API to operate on files in a Volume directly by volume ID, without mounting the Volume into a Sandbox first.
This page documents the raw HTTP and WebSocket endpoints first, then shows the equivalent helpers in the Go, Python, TypeScript SDKs, and the s0 CLI.
Requests still go through the normal gateway chain and team-scoped auth. Internally, storage-proxy lazily attaches the Volume and reclaims idle direct mounts later.
Base Model#
- Every path is resolved relative to the Volume root.
- Use absolute logical paths such as
/docs/readme.txt. - Read operations use
sandboxvolumefile:read. - Write, move, mkdir, and delete operations use
sandboxvolumefile:write.
The client helpers below call the same SandboxVolume file HTTP endpoints documented on this page. They do not require mounting the Volume into a Sandbox first.
Client Helpers#
Use these helpers when you want the direct Volume file API without constructing raw HTTP requests yourself.
goctx := context.Background() volumeID := "vol_abc123" _, err := client.MkdirVolumeFile(ctx, volumeID, "/docs", true) if err != nil { log.Fatal(err) } _, err = client.WriteVolumeFile(ctx, volumeID, "/docs/readme.txt", []byte("Hello from Volume HTTP")) if err != nil { log.Fatal(err) } data, err := client.ReadVolumeFile(ctx, volumeID, "/docs/readme.txt") if err != nil { log.Fatal(err) } fmt.Println(string(data)) info, err := client.StatVolumeFile(ctx, volumeID, "/docs/readme.txt") if err != nil { log.Fatal(err) } fmt.Printf("size=%d mode=%s\n", info.Size, info.Mode) entries, err := client.ListVolumeFiles(ctx, volumeID, "/docs") if err != nil { log.Fatal(err) } fmt.Printf("entries=%d\n", len(entries)) _, err = client.MoveVolumeFile(ctx, volumeID, "/docs/readme.txt", "/docs/readme-old.txt") if err != nil { log.Fatal(err) } _, err = client.DeleteVolumeFile(ctx, volumeID, "/docs/readme-old.txt") if err != nil { log.Fatal(err) }
Client Method Mapping#
| Operation | Go | Python | TypeScript | CLI |
|---|---|---|---|---|
| Read file | ReadVolumeFile | client.volumes.read_file | client.volumes.readFile | s0 volume files cat |
| Stat path | StatVolumeFile | client.volumes.stat_file | client.volumes.statFile | s0 volume files stat |
| List directory | ListVolumeFiles | client.volumes.list_files | client.volumes.listFiles | s0 volume files ls |
| Write file | WriteVolumeFile | client.volumes.write_file | client.volumes.writeFile | s0 volume files write |
| Create directory | MkdirVolumeFile | client.volumes.mkdir | client.volumes.mkdir | s0 volume files mkdir |
| Move or rename | MoveVolumeFile | client.volumes.move_file | client.volumes.moveFile | s0 volume files mv |
| Delete path | DeleteVolumeFile | client.volumes.delete_file | client.volumes.deleteFile | s0 volume files rm |
| Upload local file | manual local read + WriteVolumeFile | client.volumes.write_file | client.volumes.writeFile | s0 volume files upload |
| Download to local file | manual local write + ReadVolumeFile | client.volumes.read_file | client.volumes.readFile | s0 volume files download |
| Watch path | WatchVolumeFiles | client.volumes.watch_files | client.volumes.watchFiles | s0 volume files watch |
FileInfo#
| Field | Type | Description |
|---|---|---|
name | string | File or directory name |
path | string | Logical path inside the Volume |
type | string | file, dir, or symlink |
size | integer | Size in bytes |
mode | string | Unix mode bits such as 0644 |
mod_time | string | Last modification time |
is_link | boolean | Whether the entry is a symlink |
link_target | string | Symlink target when available |
Write File#
Write binary content to a file. Creates parent directories automatically when needed.
/api/v1/sandboxvolumes/{id}/files?path=/docs/readme.txt
Request Body#
Raw bytes.
bashcurl -X POST \ "$SANDBOX0_BASE_URL/api/v1/sandboxvolumes/vol_abc123/files?path=/docs/readme.txt" \ -H "Authorization: Bearer $SANDBOX0_API_KEY" \ -H "Content-Type: application/octet-stream" \ --data-binary @./README.txt
Read File#
Read a file from a Volume.
/api/v1/sandboxvolumes/{id}/files?path=/docs/readme.txt
By default the response body is raw bytes:
bashcurl \ "$SANDBOX0_BASE_URL/api/v1/sandboxvolumes/vol_abc123/files?path=/docs/readme.txt" \ -H "Authorization: Bearer $SANDBOX0_API_KEY" \ -o ./readme.txt
If you set Accept: application/json, the API returns a base64 JSON payload instead:
bashcurl \ "$SANDBOX0_BASE_URL/api/v1/sandboxvolumes/vol_abc123/files?path=/docs/readme.txt" \ -H "Authorization: Bearer $SANDBOX0_API_KEY" \ -H "Accept: application/json"
Example response:
json{ "success": true, "data": { "content": "SGVsbG8sIFZvbHVtZSEK", "encoding": "base64" } }
Create Directory#
Create a directory with the same endpoint used for file writes.
/api/v1/sandboxvolumes/{id}/files?path=/project/src&mkdir=true&recursive=true
bashcurl -X POST \ "$SANDBOX0_BASE_URL/api/v1/sandboxvolumes/vol_abc123/files?path=/project/src&mkdir=true&recursive=true" \ -H "Authorization: Bearer $SANDBOX0_API_KEY"
Stat Path#
Return metadata for a file or directory.
/api/v1/sandboxvolumes/{id}/files/stat?path=/docs/readme.txt
bashcurl \ "$SANDBOX0_BASE_URL/api/v1/sandboxvolumes/vol_abc123/files/stat?path=/docs/readme.txt" \ -H "Authorization: Bearer $SANDBOX0_API_KEY"
Example response:
json{ "success": true, "data": { "name": "readme.txt", "path": "/docs/readme.txt", "type": "file", "size": 15, "mode": "0644", "mod_time": "2026-04-03T10:00:00Z", "is_link": false } }
List Directory#
List the direct children of a directory.
/api/v1/sandboxvolumes/{id}/files/list?path=/docs
bashcurl \ "$SANDBOX0_BASE_URL/api/v1/sandboxvolumes/vol_abc123/files/list?path=/docs" \ -H "Authorization: Bearer $SANDBOX0_API_KEY"
Example response:
json{ "success": true, "data": { "entries": [ { "name": "readme.txt", "path": "/docs/readme.txt", "type": "file", "size": 15, "mode": "0644", "mod_time": "2026-04-03T10:00:00Z", "is_link": false }, { "name": "images", "path": "/docs/images", "type": "dir", "size": 0, "mode": "0755", "mod_time": "2026-04-03T10:00:00Z", "is_link": false } ] } }
Move or Rename#
Move or rename a file or directory.
/api/v1/sandboxvolumes/{id}/files/move
Request Body#
| Field | Type | Description |
|---|---|---|
source | string | Source logical path |
destination | string | Destination logical path |
bashcurl -X POST \ "$SANDBOX0_BASE_URL/api/v1/sandboxvolumes/vol_abc123/files/move" \ -H "Authorization: Bearer $SANDBOX0_API_KEY" \ -H "Content-Type: application/json" \ -d '{"source":"/docs/readme.txt","destination":"/archive/readme-old.txt"}'
Delete File or Directory#
Delete a file or recursively delete a directory.
/api/v1/sandboxvolumes/{id}/files?path=/docs/readme.txt
bashcurl -X DELETE \ "$SANDBOX0_BASE_URL/api/v1/sandboxvolumes/vol_abc123/files?path=/docs/readme.txt" \ -H "Authorization: Bearer $SANDBOX0_API_KEY"
Watch for Changes#
Watch a path for file events over WebSocket.
/api/v1/sandboxvolumes/{id}/files/watch
WebSocket Protocol#
After connecting, send JSON messages to subscribe and unsubscribe.
Client messages:
| Action | Description | Example |
|---|---|---|
subscribe | Start watching a path prefix | {"action":"subscribe","path":"/docs","recursive":true} |
unsubscribe | Stop a previous watch | {"action":"unsubscribe","watch_id":"watch-123"} |
Server messages:
| Type | Description | Example |
|---|---|---|
subscribed | Subscription accepted | {"type":"subscribed","watch_id":"watch-123","path":"/docs"} |
event | File change event | {"type":"event","watch_id":"watch-123","event":"write","path":"/docs/readme.txt"} |
unsubscribed | Watch removed | {"type":"unsubscribed","watch_id":"watch-123"} |
error | Request rejected | {"type":"error","error":"invalid path"} |
Event names currently include:
createwriteremoverenamechmodinvalidate
Client Watch Examples#
gowatchCtx, cancel := context.WithCancel(context.Background()) defer cancel() events, errs, unsubscribe, err := client.WatchVolumeFiles(watchCtx, volumeID, "/docs", true) if err != nil { log.Fatal(err) } defer func() { _ = unsubscribe() }() for { select { case ev, ok := <-events: if !ok { return } if ev.Type == "event" { fmt.Printf("%s %s\n", ev.Event, ev.Path) } case err, ok := <-errs: if ok && err != nil { log.Fatal(err) } return } }
Notes#
GET /files,GET /stat, andGET /listrequire apathquery parameter.POST /fileswithmkdir=truecreates directories instead of writing file content.DELETE /filesis recursive for directories.- File size is currently capped by the direct HTTP handler. Large transfer workflows should prefer snapshots or sync when appropriate.
Next Steps#
Volume Mounts
Mount a volume into a sandbox when processes need a normal filesystem path
Volume Sync
Keep one workspace in sync across local machines and sandboxes
Snapshots
Capture and restore point-in-time volume state
Volume Overview
Review volume lifecycle, access modes, and configuration