Documentation/docs/volume/http

#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.

go
ctx := 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#

OperationGoPythonTypeScriptCLI
Read fileReadVolumeFileclient.volumes.read_fileclient.volumes.readFiles0 volume files cat
Stat pathStatVolumeFileclient.volumes.stat_fileclient.volumes.statFiles0 volume files stat
List directoryListVolumeFilesclient.volumes.list_filesclient.volumes.listFiless0 volume files ls
Write fileWriteVolumeFileclient.volumes.write_fileclient.volumes.writeFiles0 volume files write
Create directoryMkdirVolumeFileclient.volumes.mkdirclient.volumes.mkdirs0 volume files mkdir
Move or renameMoveVolumeFileclient.volumes.move_fileclient.volumes.moveFiles0 volume files mv
Delete pathDeleteVolumeFileclient.volumes.delete_fileclient.volumes.deleteFiles0 volume files rm
Upload local filemanual local read + WriteVolumeFileclient.volumes.write_fileclient.volumes.writeFiles0 volume files upload
Download to local filemanual local write + ReadVolumeFileclient.volumes.read_fileclient.volumes.readFiles0 volume files download
Watch pathWatchVolumeFilesclient.volumes.watch_filesclient.volumes.watchFiless0 volume files watch

FileInfo#

FieldTypeDescription
namestringFile or directory name
pathstringLogical path inside the Volume
typestringfile, dir, or symlink
sizeintegerSize in bytes
modestringUnix mode bits such as 0644
mod_timestringLast modification time
is_linkbooleanWhether the entry is a symlink
link_targetstringSymlink target when available

Write File#

Write binary content to a file. Creates parent directories automatically when needed.

POST

/api/v1/sandboxvolumes/{id}/files?path=/docs/readme.txt

Request Body#

Raw bytes.

bash
curl -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.

GET

/api/v1/sandboxvolumes/{id}/files?path=/docs/readme.txt

By default the response body is raw bytes:

bash
curl \ "$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:

bash
curl \ "$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.

POST

/api/v1/sandboxvolumes/{id}/files?path=/project/src&mkdir=true&recursive=true

bash
curl -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.

GET

/api/v1/sandboxvolumes/{id}/files/stat?path=/docs/readme.txt

bash
curl \ "$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.

GET

/api/v1/sandboxvolumes/{id}/files/list?path=/docs

bash
curl \ "$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.

POST

/api/v1/sandboxvolumes/{id}/files/move

Request Body#

FieldTypeDescription
sourcestringSource logical path
destinationstringDestination logical path
bash
curl -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.

DELETE

/api/v1/sandboxvolumes/{id}/files?path=/docs/readme.txt

bash
curl -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.

GET

/api/v1/sandboxvolumes/{id}/files/watch

WebSocket Protocol#

After connecting, send JSON messages to subscribe and unsubscribe.

Client messages:

ActionDescriptionExample
subscribeStart watching a path prefix{"action":"subscribe","path":"/docs","recursive":true}
unsubscribeStop a previous watch{"action":"unsubscribe","watch_id":"watch-123"}

Server messages:

TypeDescriptionExample
subscribedSubscription accepted{"type":"subscribed","watch_id":"watch-123","path":"/docs"}
eventFile change event{"type":"event","watch_id":"watch-123","event":"write","path":"/docs/readme.txt"}
unsubscribedWatch removed{"type":"unsubscribed","watch_id":"watch-123"}
errorRequest rejected{"type":"error","error":"invalid path"}

Event names currently include:

  • create
  • write
  • remove
  • rename
  • chmod
  • invalidate

Client Watch Examples#

go
watchCtx, 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, and GET /list require a path query parameter.
  • POST /files with mkdir=true creates directories instead of writing file content.
  • DELETE /files is 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