Skip to content

Docs

Panes runs Windows machines for AI agents. Each starts in a third of a second from a template held in memory, and your code drives it with commands, files, screenshots, mouse and keyboard.

On this page

Quickstart

  1. Sign in. New organizations get $5 of credit, about 50 machine-hours.
  2. Create an API key on the API Keys page and set it as PANES_API_KEY.
  3. Install an SDK and start a machine:
$npm install panes-sdk
import { Panes } from "panes-sdk";

const panes = new Panes();
const machine = await panes.machines.create({ template: "windows" });

const result = await machine.exec("(Get-CimInstance Win32_OperatingSystem).Caption");
console.log(result.stdout);

await machine.delete();

SDKs

TypeScript (Node 18 or later) and Python (3.9 or later). Both read PANES_API_KEY from the environment, and PANES_BASE_URL if you set it (otherwise https://api.usepanes.com). Errors throw PanesError with the HTTP status.

import { Panes } from "panes-sdk";
const panes = new Panes({ apiKey: process.env.PANES_API_KEY });

Machines

A machine is a Windows desktop, 2 vCPU with the memory its template has. It starts running, signed in, from a template or a snapshot. The windows template is Windows Server 2025 with the desktop experience, Microsoft Edge and PowerShell, 4 GB.

const machine = await panes.machines.create({
  template: "windows",
  name: "invoice-run",      // optional
  autoStopMinutes: 30,      // optional; 0 keeps it running
});
console.log(machine.id, machine.state, machine.timingsMs);

const all = await panes.machines.list();
await machine.stop();       // saves memory and disk; billing stops
await machine.start();      // every window where it was
await machine.delete();     // erased, with its key (snapshots and forks keep their own)

Commands

exec runs a PowerShell script, or a program with its arguments, and returns its exit code and output. Commands run as SYSTEM by default. With desktop: true they run in the signed-in session, so their windows show on the screen.

const r = await machine.exec("Get-Process | Sort CPU -Desc | Select -First 5");
console.log(r.exitCode, r.stdout, r.stderr, r.ms);

await machine.exec(["C:/tools/report.exe", "--out", "C:/work/out.csv"], { timeoutS: 600 });
await machine.exec("Start-Process msedge https://example.com", { desktop: true });

// Pass values as PowerShell parameters, never quoted into the script
await machine.exec("param($Name) Set-Content C:/work/name.txt $Name", { params: { Name: userInput } });

A command that runs past its timeout is stopped and returns with timedOut set.

Files

Read and write any absolute path. A write lands whole or not at all, and creates the folders it needs.

await machine.files.write("C:/work/data.csv", "a,b\n1,2\n");
const bytes = await machine.files.read("C:/work/out.xlsx");
const text = await machine.files.readText("C:/work/log.txt");

Screen and input

Screenshots are PNGs at the desktop's real size, and input coordinates are pixels on that image, so a model can work from what it sees.

const png = await machine.screenshot();
await machine.click(640, 360);
await machine.click(640, 360, { button: "right" });
await machine.click(200, 120, { double: true });
await machine.move(800, 500);
await machine.scroll(3);            // notches; positive scrolls down
await machine.type("Quarterly report");
await machine.key("ctrl", "s");     // a chord

Viewer links

viewer() returns a link that opens the desktop in any browser for an hour, to watch an agent work or take over. Anyone with the link can use the machine, so share it only with people you trust. An open viewer keeps the machine from stopping when idle.

const url = await machine.viewer();

Snapshots and forks

A snapshot saves a running machine's memory and disk; the machine pauses for about 3 seconds and keeps running. Start new machines from it later. A fork snapshots a machine and starts copies at once, open windows and all.

const snap = await machine.snapshot();
const again = await panes.snapshots.start(snap.id);

const [a, b, c] = await machine.fork(3);   // 1 to 32 copies
await panes.snapshots.delete(snap.id);

Templates

Set up a machine the way you want it (apps installed, signed in), then save it as your template. The machine becomes the template, and every machine you start from it starts that way, in a third of a second. Your templates are encrypted and only your organization can use them.

const base = await panes.machines.create({ template: "windows" });
await base.files.write("C:/setup/python.exe", installer);
await base.exec("Start-Process C:/setup/python.exe '/quiet InstallAllUsers=1' -Wait");
const mine = await base.saveTemplate("python");   // base is gone after this

const worker = await panes.machines.create({ template: mine.id });
const templates = await panes.templates.list();

Auto-stop

A machine with no commands, file transfers, screenshots, input or open viewer for 15 minutes stops, keeping its memory and disk, and billing stops with it. start() resumes it as it was. Set autoStopMinutes when you create a machine, change it with setAutoStop(minutes), or use 0 to keep it running.

Usage and credit

Running machines cost $0.025 per vCPU-hour and $0.0125 per GB-hour of memory, billed by the second: $0.10 an hour for 2 vCPU and 4 GB. Stopped machines are free. When credit runs out, running machines stop and keep their state, and new ones are refused until you add credit.

const account = await panes.account();   // balance_usd, running, limits, prices
const usage = await panes.usage({ from: "2026-11-01T00:00:00Z" });
for (const m of usage.machines) console.log(m.machine, m.seconds, m.usd);

Errors

Errors are JSON, {"error": "…"}, with these statuses:

StatusMeaning
400The request is malformed. The message says which field.
401The API key is missing or wrong.
402Out of credit. Running machines have stopped and kept their state.
404No such machine, snapshot or template in your organization.
409The machine is in the wrong state for this, for example stopped, or busy with a snapshot.
429A limit: 5 running machines, or 20 machines, 20 snapshots or 2 templates kept.
503No server has room right now. Try again in a minute.

HTTP API

Every call takes Authorization: Bearer pk_… at https://api.usepanes.com. Bodies are JSON, except file uploads.

CallWhat it does
POST /v1/machines{"template", "name", "auto_stop_minutes"}: start a machine
GET /v1/machinesList your machines
GET /v1/machines/{id}One machine
PATCH /v1/machines/{id}{"auto_stop_minutes"}: 0 (never) to 10080
DELETE /v1/machines/{id}Erase the machine and its key
POST /v1/machines/{id}/exec{"script"} (PowerShell) or {"argv"}, with desktop, timeout_s, stdin, params
GET /v1/machines/{id}/files?path=Download a file
PUT /v1/machines/{id}/files?path=Upload a file; the body is its bytes
GET /v1/machines/{id}/screenshotThe screen as a PNG
POST /v1/machines/{id}/input{"actions": [...]}: clicks, moves, scrolling, typing and keys
POST /v1/machines/{id}/viewerA one-hour link to watch and use the desktop
POST /v1/machines/{id}/stopSave memory and disk, then stop
POST /v1/machines/{id}/startResume where it stopped
POST /v1/machines/{id}/fork{"count"}: 1 to 32 running copies
POST /v1/machines/{id}/snapshotSave the machine's state, keep it running
POST /v1/machines/{id}/template{"name"}: turn the machine into your template
GET /v1/snapshotsYour snapshots
POST /v1/snapshots/{id}/machines{"name", "auto_stop_minutes"}: start a machine from a snapshot
DELETE /v1/snapshots/{id}Erase a snapshot
GET /v1/templatesTemplates you can start from
DELETE /v1/templates/{id}Erase one of your templates
GET /v1/accountCredit, limits and prices
GET /v1/usage?from=&to=Spending per machine; RFC 3339 times, this month by default