Docs
API reference
The shed page uses this API, and so can your agents and scripts. Everything is JSON over HTTPS at https://shedix.dev/api.
For a walk-through an agent can follow, see the agents' guide.
Authentication
Send a token in the Authorization header. Make one on your profile, or use a Copy prompt button, which
puts a new one in a ready prompt.
curl -sS https://shedix.dev/api/machines -H "Authorization: Bearer shed_..."
A token can do anything you can do with your machines, except make more tokens. It stops working a year after its last use. Every call made with it is listed on your profile, where you can revoke it.
Machines
| Method | Path | Does |
|---|---|---|
| GET | /api/machines |
Your machines, newest first: {"machines": [...]}. Add ?project=acme/web for one project's |
| POST | /api/machines |
Create one. Body: size, and optionally stack, diskGb, sshKey, project, label, and image for the plain stack. Add ?wait=N to answer once it's ready, or after N seconds (up to 300) |
| GET | /api/machines/:name |
One machine. ?wait=N as above |
| POST | /api/machines/:name/start |
Start a stopped machine. ?wait=N as above |
| POST | /api/machines/:name/stop |
Stop it. Files are kept |
| DELETE | /api/machines/:name |
Delete it and its disk |
| PATCH | /api/machines/:name |
{"keepAwake": true} keeps it from going to sleep; false lets it sleep again. {"project": "acme/web"} names what it's for, {"label": "api box"} gives it a name of your own; null clears either |
| POST | /api/machines/:name/copy |
New machines from its whole disk as it is now (files, installed tools, and every sign-in kept on it): {"count": 1–5, "label": "…"}. Answers within about 30 seconds with {"machines": [...], "flushed": true}. One copy is a disk clone, ready in about a minute; several wait for a snapshot (setup: "copying") and are ready in three to six. flushed is false when a running source didn't confirm it wrote its newest files to disk first |
| POST | /api/machines/:name/ssh-keys |
Add a public key to this machine only: {"key": "ssh-ed25519 AAAA..."} |
Sizes: s, m, l, xl, or custom with cpus (2 to 16), ramGb and diskGb. Stacks: web (the default),
python, systems, and plain: the operating system alone, reached over SSH, Ubuntu 24.04 unless image names another
free Linux from Google as project/family (from ubuntu-os-cloud, debian-cloud, rocky-linux-cloud,
almalinux-cloud, centos-cloud, opensuse-cloud). Prices are on the pricing page.
Create and wait
curl -sS -X POST "https://shedix.dev/api/machines?wait=240" \
-H "Authorization: Bearer $SHED_TOKEN" -H "Content-Type: application/json" \
-d "{\"size\": \"m\", \"stack\": \"web\", \"project\": \"acme/web\", \"sshKey\": \"$(cat ~/.config/shed/id_ed25519.pub)\"}"
A machine
{
"name": "arnav-3f2a1b",
"status": "running",
"setup": "ready",
"ready": true,
"size": "m",
"stack": "web",
"cpus": 4,
"ramGb": 8,
"diskGb": 45,
"ip": "34.66.118.27",
"pricePerHour": 0.16,
"awakeHours": 1.25,
"costSoFar": 0.2,
"keepAwake": false,
"project": "acme/web",
"label": "api and web",
"activeAt": "2026-11-01T09:12:30.000Z",
"sleepsAt": "2026-11-01T10:12:30.000Z",
"access": {
"user": "arnav",
"host": "arnav-3f2a1b.shedix.dev",
"ssh": "ssh arnav@arnav-3f2a1b.shedix.dev",
"terminal": "https://….trycloudflare.com/<secret>/",
"ide": "https://….trycloudflare.com",
"box": "https://….trycloudflare.com/<secret>",
"password": "<the machine's secret>",
"sshKeys": ["ssh-ed25519 AAAA… you@laptop"]
},
"agents": { "claude": "ready", "codex": "off" }
}
| Field | Meaning |
|---|---|
status |
starting, running, stopping, stopped or deleting |
setup |
While it runs, booting then ready; copying for a copy waiting for its snapshot; otherwise null |
ready |
Running, with its terminal up. SSH works from here on |
name |
The person's name and a random end (arnav-3f2a1b; machines made before 2026-11-02: shed-3f2a1b). It never changes |
ip |
Public IPv4; it changes every time the machine starts |
access.host |
The machine's name on the internet, <name>.shedix.dev, once it points at this start's address; it follows the machine to every new IP (null until then) |
access.user, access.ssh |
Who to log in as (the person's name on machines made from 2026-11-02; dev before), and the SSH command by name |
activeAt, sleepsAt |
When it was last in use, and when it goes to sleep if it stays unused (null while kept awake) |
sleptAt |
When it went to sleep, while it's asleep |
project |
What it's for, as named on create or with PATCH, so an agent can find it again; null if unnamed. Up to 80 letters, digits, dots, dashes, underscores or slashes, stored lower-case |
label |
A name of your own for it, up to 60 characters; the machine's name never changes |
image |
What it runs: shed's image, or the plain OS |
access.terminal, access.ide |
The web terminal and VS Code in the browser; both carry or need the machine's secret |
access.box |
The machine's files-and-ports server, below |
agents |
Claude Code and Codex: off (not signed in), ready, allow (waiting for an answer in its terminal) or on (reachable from the phone app) |
Your profile
| Method | Path | Does |
|---|---|---|
| GET | /api/me |
Who you are, with handle: the name your machines start with (arnav in arnav-3f2a1b) |
| PATCH | /api/me |
{handle}: change it, for machines you make from now on. 2 to 16 lowercase letters or digits, starting with a letter |
| GET | /api/history |
Every machine you've had, deleted ones too, with events, hours and estimated cost |
| GET, POST | /api/ssh-keys |
Your public keys. Add one with {"key": "..."}, or import your GitHub keys with {"github": "username"}. New keys go on every machine you have |
| DELETE | /api/ssh-keys/:id |
Remove a key from your profile |
| GET | /api/tokens |
Your working tokens; ?all=1 adds revoked and expired ones |
| PATCH | /api/tokens/:id |
Rename one: {"name": "laptop"} |
| GET | /api/tokens/:id/activity |
The latest calls made with it |
| DELETE | /api/tokens/:id |
Revoke it at once |
Tokens are made on the shed page, not through the API.
A machine's files and ports
Each machine runs a small server at access.box. The address carries the machine's secret, so it needs no other
sign-in; keep it private. It works with files under ~/work only.
| Method | Path | Does |
|---|---|---|
| GET | /list?path=dir |
A folder's contents |
| GET | /file?path=file |
A file; add &download=1 to save it |
| GET | /archive?path=dir |
A folder as .tar.gz, without node_modules and .venv |
| PUT | /file?path=dir/name |
Upload the request body as a file, up to 100 MB. Add &replace=1 to overwrite |
| POST | /folder?path=dir/name |
Make a folder |
| GET | /ports |
What's listening, and any preview links |
| POST | /preview?port=3000 |
Make a public preview link for a port; it appears in /ports within seconds |
| DELETE | /preview?port=3000 |
Stop sharing it |
Errors
Errors come back as {"error": "..."} with a status: 400 (a bad request), 401 (no token, or a revoked or expired
one), 403 (not allowed), 404 (no such machine), 409 (the wrong state, like starting a running machine) or 502 (the
cloud failed; try again).