shellbox SSH API
Programmatic Linux VM management over SSH. No REST endpoints, no API tokens, no SDK.
Your SSH key authenticates every call. Add --json to any command for structured output.
# Overview
Every shellbox operation is an SSH command. Append --json for machine-readable JSON output. The protocol is SSH — use any language that can shell out or has an SSH library.
# human-readable
$ ssh shellbox.dev list
# machine-readable
$ ssh shellbox.dev list --json
{"boxes": [{"name": "mybox", "state": "running", ...}]}
No API keys to rotate. No OAuth flows. No request signing. Your SSH key is the credential.
# Authentication
Your SSH public key is your identity. The first time you connect, an account is created automatically. No signup, no email, no password.
- Auth method: SSH public key (ed25519, RSA, etc.)
- Endpoint:
shellbox.dev port 22
- One key = one account. Link additional keys with
key add.
# all commands go through a single endpoint
$ ssh shellbox.dev <command> [args] [--json]
# Quickstart
# create a box
$ ssh shellbox.dev create mybox --json
{
"name": "mybox",
"id": "a1b2c3d4-...",
"size": 1,
"backend": "fc",
"url": "https://mybox-a1b2c3d4.shellbox.dev",
"email": "mybox-a1b2c3d4@in.shellbox.dev",
"connect": "ssh mybox@shellbox.dev"
}
# connect (interactive SSH session)
$ ssh mybox@shellbox.dev
# run a command non-interactively
$ ssh mybox@shellbox.dev -- uname -a
Linux mybox 6.1.0 ...
# delete when done
$ ssh shellbox.dev delete mybox --json
{"name": "mybox", "status": "deleted"}
# Box Management
create <name> [xN] [--fc|--ch] --json
Create a new stock box. Size x1–x8 (default x1). Backend: Firecracker (default) or Cloud Hypervisor.
Response
name string · id string · size integer · backend fc | ch · url string · email string · connect string
list --json
List all your boxes.
Response
boxes array of:
name string · id string · size integer · source stock | oci · backend fc | ch
state running | stopped | stopping · mode stop | keepalive | wakeup | cron
url string | null · email string | null
idle_timeout_min integer | null · cron_interval_min integer | null · cron_runtime_min integer | null
show <name> --json
Detailed box metadata.
Response
name string | null · id string · state running | stopped | stopping
size integer · backend fc | ch · backend_label string · source stock | oci
url string | null · email string | null · mode stop | keepalive | wakeup | cron
image_ref string | null · image_digest string | null
duplicate <source> <name> --json
Clone a stopped box (inherits size).
Response
name string · source string · size integer · url string · connect string
rename <name> <new-name> --json
Rename a box.
Response
old_name string · new_name string · url string · email string
stop <name> --json
Force-stop a box (clears keepalive, preserves wakeup/cron).
Response
name string · status stopping | already_stopped | already_stopping · mode_preserved wakeup | cron | null
delete <name> --json
Delete a box permanently.
Response
name string · status "deleted"
# Modes
Control what happens when you disconnect or when traffic arrives.
keepalive <name> --json
Toggle keepalive — box stays running after disconnect.
Response
name string · keepalive boolean · box_stopped boolean · box_started boolean
wakeup <name> [idle_minutes] --json
Toggle wakeup — box auto-starts on HTTP request, stops after idle timeout.
Response
name string · wakeup boolean · idle_timeout_min integer | null · box_stopped boolean
cron <name> <interval_min> [runtime_min] --json
Toggle cron — box wakes on schedule, hits webhook, stops.
Response
name string · cron boolean · interval_min integer | null · runtime_min integer | null · box_stopped boolean
# OCI Images
Create boxes from Docker/OCI images. The image is prepared into a bootable VM with SSH access.
image prepare <image-ref> --json
Prepare an OCI image for use. Asynchronous — poll with image status.
Response
display_ref string · digest string · status string · state string · error string | null
image status <image-ref> --json
Check preparation status.
Response
display_ref string · canonical_ref string · status string | null · digest string
architecture string · guest_layer_version string · error string | null
create-from-oci <name> <image-ref> [xN] [--fc|--ch] --json
Create a box from a prepared OCI image.
Response
name string · id string · size integer · backend fc | ch
image_ref string · image_digest string · url string · email string · connect string
# Billing & Payments
billing --json
Account balance, usage costs, and per-box burn rates.
Response
balance number · funds_added number · funds_refunded number · usage_cost number
hourly_rate number · remaining_hours number | null
pending_refunds array of {amount, status, created_at}
boxes array of {name, size, state, hourly_rate}
funds <amount> --json
Add funds (min $10). Returns a checkout URL.
Response
amount number · checkout_url string
payments --json
Payment history.
Response
pending array of {amount, created_at}
recent array of {amount, timestamp}
refund <amount> --json
Request a refund (within 3 months of payment).
Response
amount number · status "pending"
promocode <code> --json
Redeem a promo code for free credits.
Response
code string · added number · balance number
# Key Management
key list --json
List SSH keys linked to your account.
Response
keys array of:
fingerprint string · type string · comment string · current boolean
key add "<public-key>" --json
Link an additional SSH key to your account.
Response
fingerprint string · type string · comment string · action "added"
key remove <fingerprint> --json
Remove an SSH key from your account.
Response
fingerprint string · action "removed"
# Connecting to Boxes
Box connections use SSH with the box name as the username:
# interactive shell
$ ssh mybox@shellbox.dev
# run a command
$ ssh mybox@shellbox.dev -- ls /root
# file transfer
$ scp file.txt mybox@shellbox.dev:/root/
$ scp mybox@shellbox.dev:/root/output.txt ./
$ sftp mybox@shellbox.dev
# port forwarding
$ ssh -L 8080:localhost:8080 mybox@shellbox.dev
$ ssh -R 9090:localhost:9090 mybox@shellbox.dev
Connecting to a stopped box automatically starts it. Disconnecting pauses it (unless keepalive/wakeup/cron is set).
# Scripting Patterns
Bash
#!/bin/bash
# create a box, run a build, tear down
BOX=$(ssh shellbox.dev create ci-$RANDOM --json | jq -r .name)
ssh "$BOX@shellbox.dev" -- bash -c 'cd /root && git clone ... && make test'
ssh shellbox.dev delete "$BOX" --json
Python
# using subprocess
import subprocess, json
result = subprocess.run(
["ssh", "shellbox.dev", "list", "--json"],
capture_output=True, text=True
)
boxes = json.loads(result.stdout)["boxes"]
running = [b for b in boxes if b["state"] == "running"]
Agent / LLM tool use
# give your agent a sandbox
$ ssh shellbox.dev create agent-sandbox --json
# agent runs commands via SSH
$ ssh agent-sandbox@shellbox.dev -- pip install -r requirements.txt
$ ssh agent-sandbox@shellbox.dev -- python run_task.py
# public URL for webhooks/preview
$ ssh agent-sandbox@shellbox.dev -- 'python -m http.server 80 &'
# https://agent-sandbox-a1b2c3d4.shellbox.dev is live
Full schema reference available at runtime:
$ ssh shellbox.dev help --json
Stability guarantee: The --json output is a stable interface. Fields are only added, never removed or renamed. Types don't change. If a breaking change is ever necessary, it will ship under a new flag (--json-v2) and the original --json output will continue to work unchanged.