Cloud API Reference

Base URL: https://api.smolmachines.com

All endpoints require Authorization: Bearer <api_key> header.

Machines

Create a machine

POST /v1/machines
{
  "name": "my-machine",
  "source": { "type": "image", "reference": "alpine" },
  "resources": { "cpus": 1, "memoryMb": 256, "diskGb": 20 },
  "network": { "mode": "open" },
  "env": { "APP_ENV": "production" },
  "workdir": "/workspace",
  "autoStopSeconds": 300,
  "ttlSeconds": 3600,
  "ephemeral": false,
  "mounts": [{ "volume": "data-vol", "mountPath": "/data" }]
}

All fields except source are optional. Returns the machine object with state: "stopped".

List machines

GET /v1/machines

Returns an array of all your machines.

Get a machine

GET /v1/machines/:id

Start a machine

POST /v1/machines/:id/start

Stop a machine

POST /v1/machines/:id/stop

Compute billing stops immediately. Storage persists.

Delete a machine

DELETE /v1/machines/:id

Exec

Execute a command

POST /v1/machines/:id/exec
{
  "command": "echo hello",
  "env": { "MY_VAR": "value" },
  "cwd": "/workspace",
  "timeoutSeconds": 30,
  "stdin": "input data\n",
  "stream": false
}

command can be a string (runs via sh -lc) or an array (argv form: ["python3", "app.py"]).

If the machine is stopped, it auto-starts before executing.

TIP

`exec` calls — running commands inside a machine — are never charged and never rate-limited. Run as many as you want.

Response:

{
  "stdout": "hello\n",
  "stderr": "",
  "exitCode": 0,
  "durationMs": 14,
  "machineId": "mach-abc123"
}

Streaming exec (SSE)

Set "stream": true to receive Server-Sent Events as the command runs.

Execute code

POST /v1/machines/:id/code
{
  "language": "python",
  "code": "print(sum(range(100)))"
}

Supported languages: python, javascript/node.

Files

Upload a file

PUT /v1/machines/:id/files/{path}

Body: raw file bytes.

Download a file

GET /v1/machines/:id/files/{path}

Sessions

Sessions maintain environment variables and working directory across multiple exec calls.

Create a session

POST /v1/machines/:id/sessions
{
  "cwd": "/workspace",
  "env": { "PROJECT": "myapp" }
}

Execute in a session

POST /v1/machines/:id/sessions/:sessionId/exec

Same body as regular exec. Session env and cwd are inherited (request overrides).

List sessions

GET /v1/machines/:id/sessions

Delete a session

DELETE /v1/machines/:id/sessions/:sessionId

Events

Get machine events

GET /v1/machines/:id/events

Returns lifecycle events (start failures, rescheduling, etc.) for debugging.

Volumes

Create a volume

POST /v1/volumes
{
  "name": "my-data",
  "sizeGb": 10
}

List volumes

GET /v1/volumes

Delete a volume

DELETE /v1/volumes/:id

Mount volumes when creating a machine using the mounts field.

Usage

Query usage

GET /v1/usage?tenantId=TENANT&from=ISO_DATE&to=ISO_DATE

Response:

{
  "tenantId": "acme-corp",
  "totalUptimeSeconds": 86400,
  "cpuHours": 48.0,
  "memoryGbHours": 12.0,
  "machineCount": 5,
  "execCount": 230,
  "execDurationMs": 45000
}

API Keys

Create an API key

POST /v1/apikeys
{
  "description": "CI deploy key",
  "scopes": ["machine:create", "machine:exec"],
  "expiresInDays": 30
}

List API keys

GET /v1/apikeys

Revoke an API key

DELETE /v1/apikeys/:id

Health

GET /health

Public endpoint, no auth required.

Error responses

All errors return a plain text body with an appropriate HTTP status code:

StatusMeaning
400Bad request (validation failed)
401Unauthorized (missing or invalid key)
403Forbidden (insufficient scopes)
404Not found (or belongs to another tenant)
409Conflict (duplicate name)
422Unprocessable (quota exceeded, no capacity)
429Rate limited
500Internal server error