Sandboxes
Everything below is a plain authenticated HTTP call to https://gateway.codegraff.com. No SDK is required. Send your cg_sk_ key as the bearer token on every request.
Create a sandbox
POST /v1/sandboxes boots a VM and returns its id plus the allocated spec. autoStopMinutes (default 30) is the inactivity window after which it auto-stops to stop billing.
const GATEWAY = "https://gateway.codegraff.com";
const headers = {
Authorization: `Bearer ${process.env.CODEGRAFF_API_KEY}`, // cg_sk_...
"Content-Type": "application/json",
};
const sb = await fetch(`${GATEWAY}/v1/sandboxes`, {
method: "POST",
headers,
body: JSON.stringify({ language: "javascript", autoStopMinutes: 30 }),
}).then((r) => r.json());
console.log(sb.id, sb.cpu, sb.memory, sb.disk); // id + allocated specimport os, requests
GATEWAY = "https://gateway.codegraff.com"
headers = {"Authorization": f"Bearer {os.environ['CODEGRAFF_API_KEY']}"} # cg_sk_...
sb = requests.post(
f"{GATEWAY}/v1/sandboxes",
headers=headers,
json={"language": "javascript", "autoStopMinutes": 30},
).json()
print(sb["id"], sb["cpu"], sb["memory"], sb["disk"])Run a command
POST /v1/sandboxes/:id/exec runs a shell command and, by default, blocks until it finishes, returning exitCode and combined stdout/stderr in result. cwd is optional (defaults to the home directory).
const { exitCode, result } = await fetch(
`${GATEWAY}/v1/sandboxes/${sb.id}/exec`,
{ method: "POST", headers, body: JSON.stringify({ command: "node -v" }) },
).then((r) => r.json());
console.log(exitCode, result); // 0 "v22.x\n"r = requests.post(
f"{GATEWAY}/v1/sandboxes/{sb['id']}/exec",
headers=headers,
json={"command": "node -v"},
).json()
print(r["exitCode"], r["result"]) # 0 "v22.x\n"~95s synchronous limit
exec that runs longer is aborted at ~95s with an exec_timeout error (HTTP 504). Use an async command for anything that might run that long, such as installs, test suites, builds, or clones.Long-running commands (async)
Pass "async": true and the command launches detached inside the sandbox; the call returns immediately with an execId. You then poll GET /v1/sandboxes/:id/exec/:execId with short calls that never approach the cap. The job keeps running across polls and client disconnects until it finishes or the sandbox stops.
// Launch: returns { execId, state: "running" } right away
const { execId } = await fetch(`${GATEWAY}/v1/sandboxes/${sb.id}/exec`, {
method: "POST",
headers,
body: JSON.stringify({ command: "npm ci && npm test", async: true }),
}).then((r) => r.json());
// Poll until done; each call is well under a second
let job;
do {
await new Promise((r) => setTimeout(r, 1000));
job = await fetch(
`${GATEWAY}/v1/sandboxes/${sb.id}/exec/${execId}?full=1`,
{ headers },
).then((r) => r.json());
} while (job.state !== "completed");
console.log(job.output, "→ exit", job.exitCode);import time
launch = requests.post(
f"{GATEWAY}/v1/sandboxes/{sb['id']}/exec",
headers=headers,
json={"command": "npm ci && npm test", "async": True},
).json()
exec_id = launch["execId"]
while True:
job = requests.get(
f"{GATEWAY}/v1/sandboxes/{sb['id']}/exec/{exec_id}",
headers=headers,
params={"full": 1},
).json()
if job["state"] == "completed":
break
time.sleep(1)
print(job["output"], "-> exit", job["exitCode"])A poll returns state (running or completed), exitCode (once done), and output, which contains the last 64 KB of stdout/stderr by default, or pass ?tail=N for N bytes / ?full=1 for everything. To stop a job early, DELETE /v1/sandboxes/:id/exec/:execId kills it and cleans up.
Files
Move files in and out with base64-encoded bodies:
| Call | Body → Response |
|---|---|
POST .../upload | { path, contentBase64 } → { ok: true } |
POST .../download | { path } → { contentBase64 } |
curl -X POST https://gateway.codegraff.com/v1/sandboxes/$ID/upload \
-H "Authorization: Bearer cg_sk_your_key" -H "Content-Type: application/json" \
-d "{\"path\":\"/home/user/app.js\",\"contentBase64\":\"$(base64 < app.js)\"}"Idle stop and garbage collection
Idle sandboxes stop automatically; they do not delete themselves
autoStopMinutes inactivity window (30 minutes by default). Auto-stop ends compute billing and preserves the filesystem, but the stopped sandbox remains in your account until you explicitly call DELETE /v1/sandboxes/:id.The inactivity timer tracks external sandbox activity. Gateway interactions such as exec and file/toolbox calls refresh activity; a process merely running in the background does not. For an unattended build, server, or async job, choose an autoStopMinutes value longer than the expected runtime. Setting it to 0 disables idle auto-stop, but compute metering continues and the gateway still force-stops the sandbox if the account runs out of credit.
| Condition | What Codegraff does |
|---|---|
| Running but idle | Auto-stops after autoStopMinutes. Memory and processes are cleared; disk files are preserved. |
| Running without credit | The billing janitor force-stops it on its next successful pass (scheduled once per minute) so more compute debt cannot accrue. |
| Stopped or archived | The janitor settles residual compute and releases the reservation. Compute no longer accrues; retained storage is billed only above the free 5 GiB allowance. The sandbox is not automatically deleted by Codegraff. |
DELETE | Settles any final usage, removes the billing meter, and permanently destroys the sandbox and its files. |
For disposable workloads, call DELETE /v1/sandboxes/:id in a cleanup or finally block so a failed task does not leave a stopped sandbox behind.
Lifecycle
| Call | Effect |
|---|---|
GET /v1/sandboxes | List your sandboxes. |
GET /v1/sandboxes/:id | State + spec for one sandbox. |
POST .../stop | Stop the VM and settle compute billing. Disk state is preserved. |
POST .../start | Resume a stopped sandbox. |
DELETE /v1/sandboxes/:id | Settle final billing and destroy it for good. |
Each sandbox is private to the key's owner. Calls against a sandbox you do not own return 404.
Billing
Compute is billed per second of running time and debited from your credit balance. The reservation for a fresh sandbox covers its full autoStopMinutes window; a per-minute meter then charges actual uptime (so a long-running or restarted box is billed for exactly the seconds it ran, never the wall-clock gap while stopped).
| Resource | Rate |
|---|---|
| vCPU | 19.6 µUSD/s each |
| Memory | 6.3 µUSD/s per GiB |
| Storage | 0.042 µUSD/s per GiB (first 5 GiB free) |
What it costs
65 µUSD/s, about $0.0039/min or $0.23/hour while running. A stopped sandbox bills only storage (free under 5 GiB) until you delete it. Hit GET /v1/sandboxes/:id/meter for live cost, or watch the dashboard. Costs are in micro-USD (1,000,000 µUSD = $1).Using the SDK
If you embed the agent with the TypeScript or Python SDK, the same sandboxes are wrapped in a higher-level client: graff.createSandbox({...}) / graff.create_sandbox(...), then box.exec(...), box.upload(...) / download, and box.stop() / start() / destroy(). They authenticate with the same cg_sk_ key and bill identically. Reach for the raw HTTP API above when you're not in Node or Python, or when you want background exec + polling.