API Mode
This guide covers running heyvm as an HTTP API server and interacting with sandboxes programmatically. This is useful for integrating sandboxes into your own tooling, CI pipelines, or building platforms on top of heyvm.
Prerequisites
- heyvm installed (Quickstart)
- A backend configured and running
1. Start the API server
Run heyvm in API-only mode:
heyvm --api --port 3000
The server binds to 0.0.0.0:3000. Verify it's running:
curl http://localhost:3000/health{"status":"ok"}Startup options
| Flag | Description |
|---|---|
--api | Start the API server without the TUI |
--port <PORT> | Port to listen on (default: 3000) |
--debug | Enable verbose logging |
--dev | Development mode (uses localhost auth/cloud URLs) |
When running heyvm without --api (TUI mode), the API also starts in the background on port 34099.
2. Create a sandbox
curl -X POST http://localhost:3000/sandboxes \
-H "Content-Type: application/json" \
-d '{
"name": "api-demo",
"sandbox_type": "shell",
"image": "ubuntu:24.04",
"ttl_seconds": 3600
}'
The response returns a SandboxInfo object with the sandbox's id and slug:
{
"id": "a1b2c3d4-...",
"name": "api-demo",
"slug": "api-demo",
"status": "running",
...
}Create options
{
"name": "my-sandbox",
"sandbox_type": "shell",
"image": "ubuntu:24.04",
"backend_type": "msb",
"ttl_seconds": 3600,
"start_command": "nginx -g 'daemon off;'",
"working_directory": "/workspace",
"open_ports": [80, 8080],
"env_vars": { "NODE_ENV": "production" },
"setup_hooks": ["apt-get update && apt-get install -y nginx"],
"mounts": [
{
"host_path": "/home/user/project",
"sandbox_path": "/workspace",
"read_only": false
}
]
}3. List and inspect sandboxes
List all running sandboxes:
curl http://localhost:3000/sandboxes
Get details for a specific sandbox (by ID or slug):
curl http://localhost:3000/sandboxes/api-demo4. Execute commands
Run a command inside a sandbox:
curl -X POST http://localhost:3000/sandboxes/api-demo/execute \
-H "Content-Type: application/json" \
-d '{"command": "echo", "args": ["hello from the sandbox"]}'{
"output": "hello from the sandbox\n",
"exit_code": 0
}
You can run any shell command this way:
curl -X POST http://localhost:3000/sandboxes/api-demo/execute \
-H "Content-Type: application/json" \
-d '{"command": "ls", "args": ["-la", "/workspace"]}'5. Manage the sandbox lifecycle
Stop, start, restart, or delete a sandbox:
# Stop
curl -X POST http://localhost:3000/sandboxes/api-demo/stop
# Start (a stopped sandbox)
curl -X POST http://localhost:3000/sandboxes/api-demo/start
# Restart
curl -X POST http://localhost:3000/sandboxes/api-demo/restart
# Force stop
curl -X POST http://localhost:3000/sandboxes/api-demo/force-stop
# Delete
curl -X DELETE http://localhost:3000/sandboxes/api-demo6. Work with files
Upload, download, list, and delete files in a sandbox's mounted filesystem.
List files
curl "http://localhost:3000/sandboxes/api-demo/files?mount_path=/workspace"Upload a file
curl -X POST http://localhost:3000/sandboxes/api-demo/files \
-F "mount_path=/workspace" \
-F "file_path=index.html" \
-F "file=@./index.html"
Or upload via JSON with base64 content:
curl -X POST http://localhost:3000/sandboxes/api-demo/files/upload \
-H "Content-Type: application/json" \
-d '{
"mount_path": "/workspace",
"file_path": "hello.txt",
"content": "SGVsbG8gd29ybGQh"
}'Download a file
curl "http://localhost:3000/sandboxes/api-demo/files/index.html?mount_path=/workspace"Delete a file
curl -X DELETE "http://localhost:3000/sandboxes/api-demo/files/old-file.txt?mount_path=/workspace"7. Create proxy endpoints
Expose a port inside the sandbox via a proxy endpoint:
curl -X POST http://localhost:3000/sandboxes/api-demo/proxy \
-H "Content-Type: application/json" \
-d '{"port": 80}'{
"subdomain": "abc123",
"hostname": "localhost",
"sandbox_id": "a1b2c3d4-...",
"port": 80
}
In local mode, the proxy binds to a local TCP port. In production with API_HOSTNAME set, the proxy is accessible at https://<subdomain>.<hostname>.
List and delete proxies:
# List
curl http://localhost:3000/sandboxes/api-demo/proxy
# Delete
curl -X DELETE "http://localhost:3000/sandboxes/api-demo/proxy?subdomain=abc123"8. Get sandbox logs
curl "http://localhost:3000/sandboxes/api-demo/logs?limit=50"{
"logs": [
{
"timestamp": 1742659200,
"source": "stdout",
"message": "Server started on port 80"
}
],
"total": 1,
"limit": 50,
"offset": 0
}
Filter by source or level:
curl "http://localhost:3000/sandboxes/api-demo/logs?source=stderr&level=error"9. Update a sandbox
Change a sandbox's configuration while it's running:
curl -X PATCH http://localhost:3000/sandboxes/api-demo \
-H "Content-Type: application/json" \
-d '{
"start_command": "nginx -g '\''daemon off;'\''",
"env_vars": { "DEBUG": "true" },
"open_ports": [8080]
}'10. Shell sessions
Request an interactive shell session:
curl -X POST http://localhost:3000/sandboxes/api-demo/shell-session{
"connection_url": "heyo://..."
}
The returned connection_url is an iroh P2P tunnel. Connect to it with:
heyvm connect <connection_url> --shellAuthentication
By default, the API has no authentication. To enable JWT-based auth, set the JWT_SECRET environment variable:
JWT_SECRET=my-secret-key heyvm --api --port 3000
When enabled, all endpoints except /health require a Bearer token:
curl http://localhost:3000/sandboxes \
-H "Authorization: Bearer <your-jwt-token>"
Optionally set JWT_AUDIENCE to require a specific audience claim in tokens.
Environment variables
| Variable | Description |
|---|---|
JWT_SECRET | HMAC-256 signing key — enables authentication when set |
JWT_AUDIENCE | Expected audience claim in JWT tokens |
MSB_API_KEY | Microsandbox API key (required for MSB backend) |
MSB_SERVER_HOST | Microsandbox server host (default: localhost) |
API_HOSTNAME | Domain for subdomain-based proxy (e.g. example.com) |
S3_ENABLED | Enable S3 mount persistence (true or 1) |
S3_BUCKET | S3 bucket name |
AUTH_SERVER_URL | Proxy auth endpoints to this URL |
For production deployment behind a reverse proxy, see Bare metal deployment.
Quick reference
| Method | Path | Description |
|---|---|---|
| GET | /health | Health check |
| GET | /sandboxes | List sandboxes |
| POST | /sandboxes | Create sandbox |
| GET | /sandboxes/:id | Get sandbox details |
| PATCH | /sandboxes/:id | Update sandbox |
| DELETE | /sandboxes/:id | Delete sandbox |
| POST | /sandboxes/:id/start | Start sandbox |
| POST | /sandboxes/:id/stop | Stop sandbox |
| POST | /sandboxes/:id/restart | Restart sandbox |
| POST | /sandboxes/:id/execute | Run a command |
| POST | /sandboxes/:id/shell-session | Get shell connection URL |
| GET | /sandboxes/:id/files | List files |
| POST | /sandboxes/:id/files | Upload file (multipart) |
| POST | /sandboxes/:id/files/upload | Upload file (JSON/base64) |
| GET | /sandboxes/:id/files/:path | Download file |
| DELETE | /sandboxes/:id/files/:path | Delete file |
| GET | /sandboxes/:id/proxy | List proxy endpoints |
| POST | /sandboxes/:id/proxy | Create proxy endpoint |
| DELETE | /sandboxes/:id/proxy | Delete proxy endpoint |
| GET | /sandboxes/:id/logs | Get logs |
| GET | /sandboxes/:id/mounts | List mounts |
| POST | /sandboxes/:id/mounts | Add mount |
| DELETE | /sandboxes/:id/mounts/:path | Remove mount |