shed

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).