Documentation/docs/volume/http

#Volume HTTP

Use direct Volume file operations to operate on files in a Volume by volume ID, without mounting the Volume into a Sandbox first.

Start with the Go, Python, TypeScript SDKs, or the s0 CLI. The endpoint reference below is for advanced integrations and custom clients that need to call the HTTP and WebSocket API directly.

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.

SDK and CLI Quick Start#

Use these helpers for normal direct Volume file workflows.

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) } summary, err := client.UploadVolumeDirectory(ctx, volumeID, "./dist", "/apps/current") if err != nil { log.Fatal(err) } fmt.Printf("uploaded files=%d bytes=%d\n", summary.Files, summary.Bytes) 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
Upload directory/archiveUploadVolumeDirectory / ImportVolumeArchiveclient.volumes.upload_directory / client.volumes.import_archiveclient.volumes.importArchives0 volume files upload --recursive
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

Directory Upload#

Use directory upload for build artifacts, framework bundles, and other multi-file trees.

go
summary, err := client.UploadVolumeDirectory(ctx, volumeID, "./dist", "/apps/current") if err != nil { log.Fatal(err) } fmt.Printf("uploaded files=%d dirs=%d symlinks=%d bytes=%d\n", summary.Files, summary.Directories, summary.Symlinks, summary.Bytes, )

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. For SDK and CLI usage, see the quick start and method mapping above.


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.

If you set Accept: application/json, the API returns a base64 JSON payload instead:

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

For SDK and CLI usage, see the create-directory row in the method mapping above.


Stat Path#

Return metadata for a file or directory.

GET

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

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

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

For SDK and CLI usage, see the move-or-rename row in the method mapping above.


Delete File or Directory#

Delete a file or recursively delete a directory.

DELETE

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

For SDK and CLI usage, see the delete-path row in the method mapping above.


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.
  • Directory uploads use a tar stream internally and are not atomic. Deployment flows that require rollback should import into a fresh Volume or snapshot before switching traffic.
  • DELETE /files is recursive for directories.
  • File size is currently capped by the direct HTTP handler. Large transfer workflows should prefer snapshots when appropriate.

Next Steps#

Snapshots

Create and restore point-in-time volume snapshots.

Fork

Fork volumes when workflows need isolated writable branches.