Files and Processes
After you create or connect to a sandbox, use its data-plane URL and access token for file, directory, and streaming process operations.
export SANDBOX_URL="https://<route-token>.sandbox.watasuhost.com"export SANDBOX_TOKEN="..."Data-plane requests use:
Authorization: Bearer <sandbox access token>You can also call the Watasu API under /v1/sandboxes/:id/... with your Watasu API key. The runtime data-plane API is the shorter path for active sandbox work.
Write a file
Section titled “Write a file”curl "$SANDBOX_URL/runtime/v1/files?path=/workspace/hello.py" \ -X PUT \ -H "Authorization: Bearer $SANDBOX_TOKEN" \ -H "Content-Type: text/plain" \ --data-binary 'print("hello from Watasu")'For binary uploads, send the request body as bytes with an appropriate content type.
Write multiple files
Section titled “Write multiple files”Use write_files when you already have several small file payloads to upload:
curl "$SANDBOX_URL/runtime/v1/files/write_files" \ -X POST \ -H "Authorization: Bearer $SANDBOX_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "files": [ { "path": "/workspace/a.txt", "data_base64": "YWxwaGE=" }, { "path": "/workspace/b.bin", "data_base64": "AAEC" } ] }'The response includes files, one metadata object per written path. SDKs expose
this as filesystem.writeFiles(...), files.write_files(...), and
Filesystem::write_files(...).
Read a file
Section titled “Read a file”curl "$SANDBOX_URL/runtime/v1/files?path=/workspace/hello.py" \ -H "Authorization: Bearer $SANDBOX_TOKEN"Signed upload and download URLs
Section titled “Signed upload and download URLs”Use the control API when a browser, worker, or third-party service needs a short-lived file URL without a sandbox bearer token:
curl https://api.watasu.io/v1/sandboxes/123/files/upload_url \ -X POST \ -H "Authorization: Bearer $WATASU_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "path": "/workspace/input.bin", "use_signature_expiration": 900 }'
curl https://api.watasu.io/v1/sandboxes/123/files/download_url \ -X POST \ -H "Authorization: Bearer $WATASU_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "path": "/workspace/output.bin", "use_signature_expiration": 900 }'The response includes file_url.url, method, path, and expires_at.
Upload URLs accept POST with the raw file body; download URLs accept GET.
Run code
Section titled “Run code”Use the code execution route for notebook-style Python snippets. The request body uses snake_case keys:
curl "$SANDBOX_URL/runtime/v1/code/run" \ -X POST \ -H "Authorization: Bearer $SANDBOX_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "code": "print(\"hello\")\n2 + 3", "language": "python", "env_vars": { "PYTHONUNBUFFERED": "1" }, "timeout_ms": 30000 }'The response includes execution.results, execution.logs.stdout,
execution.logs.stderr, and execution.error. User-code exceptions are returned
inside execution.error; transport/runtime failures use HTTP errors. Code runs
in a persistent default Python context unless context_id selects another
context. Use either language or context_id, not both.
Code contexts
Section titled “Code contexts”Create a context:
curl "$SANDBOX_URL/runtime/v1/code/contexts" \ -X POST \ -H "Authorization: Bearer $SANDBOX_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "cwd": "/workspace", "language": "python" }'List contexts:
curl "$SANDBOX_URL/runtime/v1/code/contexts" \ -H "Authorization: Bearer $SANDBOX_TOKEN"Restart or remove a context:
curl "$SANDBOX_URL/runtime/v1/code/contexts/$CONTEXT_ID/restart" \ -X POST \ -H "Authorization: Bearer $SANDBOX_TOKEN"
curl "$SANDBOX_URL/runtime/v1/code/contexts/$CONTEXT_ID" \ -X DELETE \ -H "Authorization: Bearer $SANDBOX_TOKEN"File metadata
Section titled “File metadata”curl "$SANDBOX_URL/runtime/v1/files/stat?path=/workspace/hello.py" \ -H "Authorization: Bearer $SANDBOX_TOKEN"Move or delete a file
Section titled “Move or delete a file”curl "$SANDBOX_URL/runtime/v1/files/move?path=/workspace/hello.py" \ -X POST \ -H "Authorization: Bearer $SANDBOX_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "to_path": "/workspace/main.py" }'
curl "$SANDBOX_URL/runtime/v1/files?path=/workspace/main.py" \ -X DELETE \ -H "Authorization: Bearer $SANDBOX_TOKEN"Directories
Section titled “Directories”Create, list, and remove directories:
curl "$SANDBOX_URL/runtime/v1/directories?path=/workspace/results" \ -X POST \ -H "Authorization: Bearer $SANDBOX_TOKEN"
curl "$SANDBOX_URL/runtime/v1/directories?path=/workspace" \ -H "Authorization: Bearer $SANDBOX_TOKEN"
curl "$SANDBOX_URL/runtime/v1/directories?path=/workspace/results" \ -X DELETE \ -H "Authorization: Bearer $SANDBOX_TOKEN"Stream a process
Section titled “Stream a process”Processes run over a WebSocket at /runtime/v1/process. Send a start frame,
then read started, stdout, stderr, and final exit frames as the process
runs. Output data is base64 encoded.
{"type":"start","cmd":"python3","args":["/workspace/hello.py"],"cwd":"/workspace","timeout_ms":60000,"env":{"PYTHONUNBUFFERED":"1"}}Example server frames:
{"type":"started","pid":"proc-123","os_pid":1234,"cursor":1}{"type":"stdout","pid":"proc-123","data":"aGVsbG8K","cursor":2}{"type":"exit","pid":"proc-123","exit_code":0,"error":null,"cursor":3}Write stdin with:
{"type":"stdin","data":"aGkK"}Send a signal over the socket:
{"type":"signal","signal":"SIGTERM"}or with the REST signal helper:
curl "$SANDBOX_URL/runtime/v1/process/<pid>/signal" \ -X POST \ -H "Authorization: Bearer $SANDBOX_TOKEN" \ -H "Content-Type: application/json" \ -d '{"signal":"SIGTERM"}'List running processes with a plain HTTP request:
curl "$SANDBOX_URL/runtime/v1/process" \ -H "Authorization: Bearer $SANDBOX_TOKEN"Reconnect to a running process with
GET /runtime/v1/process/<pid>/connect?since=<cursor> as a WebSocket. The
gateway replays buffered frames after the cursor and then streams live output.
PTY sessions
Section titled “PTY sessions”Start a PTY by sending a start frame with pty size over the process
WebSocket:
{"type":"start","cmd":"/bin/bash","args":["-i","-l"],"stdin":true,"pty":{"cols":100,"rows":30}}PTY output is sent as base64 encoded pty frames. Send input with the same
stdin frame used by commands and resize with:
{"type":"resize","cols":120,"rows":40}Watch files
Section titled “Watch files”Watch a directory over WebSocket:
GET /runtime/v1/files/watch?path=/workspace&recursive=trueThe stream sends event batches:
{"type":"events","events":[{"type":"modify","path":"/workspace/hello.py"}]}The SDKs expose this as filesystem.watchDir(...), files.watch_dir(...), and
Filesystem::watch_dir(...).
Git helper routes
Section titled “Git helper routes”The data plane includes server-side Git helpers:
curl "$SANDBOX_URL/runtime/v1/git/clone" \ -X POST \ -H "Authorization: Bearer $SANDBOX_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://github.com/acme/project.git", "path": "/workspace/project", "branch": "main", "depth": 1 }'
curl "$SANDBOX_URL/runtime/v1/git/status" \ -X POST \ -H "Authorization: Bearer $SANDBOX_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "path": "/workspace/project" }'
curl "$SANDBOX_URL/runtime/v1/git/restore" \ -X POST \ -H "Authorization: Bearer $SANDBOX_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "path": "/workspace/project", "paths": ["README.md"] }'The same route family supports dangerously_authenticate, configure_user,
init, branches, create_branch, delete_branch, add, commit, reset,
restore, pull, push, remote_add, remote_get, set_config,
get_config, and checkout. API payloads use snake_case keys such as
env_vars, timeout_seconds, set_upstream, initial_branch, author_name,
and dangerously_store_credentials.