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

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

FlagDescription
--apiStart the API server without the TUI
--port <PORT>Port to listen on (default: 3000)
--debugEnable verbose logging
--devDevelopment 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-demo

4. 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-demo

6. 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> --shell

Authentication

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

VariableDescription
JWT_SECRETHMAC-256 signing key — enables authentication when set
JWT_AUDIENCEExpected audience claim in JWT tokens
MSB_API_KEYMicrosandbox API key (required for MSB backend)
MSB_SERVER_HOSTMicrosandbox server host (default: localhost)
API_HOSTNAMEDomain for subdomain-based proxy (e.g. example.com)
S3_ENABLEDEnable S3 mount persistence (true or 1)
S3_BUCKETS3 bucket name
AUTH_SERVER_URLProxy auth endpoints to this URL

For production deployment behind a reverse proxy, see Bare metal deployment.

Quick reference

MethodPathDescription
GET/healthHealth check
GET/sandboxesList sandboxes
POST/sandboxesCreate sandbox
GET/sandboxes/:idGet sandbox details
PATCH/sandboxes/:idUpdate sandbox
DELETE/sandboxes/:idDelete sandbox
POST/sandboxes/:id/startStart sandbox
POST/sandboxes/:id/stopStop sandbox
POST/sandboxes/:id/restartRestart sandbox
POST/sandboxes/:id/executeRun a command
POST/sandboxes/:id/shell-sessionGet shell connection URL
GET/sandboxes/:id/filesList files
POST/sandboxes/:id/filesUpload file (multipart)
POST/sandboxes/:id/files/uploadUpload file (JSON/base64)
GET/sandboxes/:id/files/:pathDownload file
DELETE/sandboxes/:id/files/:pathDelete file
GET/sandboxes/:id/proxyList proxy endpoints
POST/sandboxes/:id/proxyCreate proxy endpoint
DELETE/sandboxes/:id/proxyDelete proxy endpoint
GET/sandboxes/:id/logsGet logs
GET/sandboxes/:id/mountsList mounts
POST/sandboxes/:id/mountsAdd mount
DELETE/sandboxes/:id/mounts/:pathRemove mount