Workflows
The signature orl commands for deploying and connecting to GPU VMs and watching spend, plus how to script orl in CI with JSON errors and exit codes.
Every REST endpoint has an orl command (you will see it on each
API Reference page). These are the ones you reach for
most.
Deploy a VM
orl deploy <name> picks a GPU shape from live availability and creates a VM.
It is interactive in a terminal and fully scriptable with flags. Every VM needs
at least one --ssh-key (orl ssh-keys list prints the ids).
# Guided: choose the GPU from what has capacity
orl deploy trainer --ssh-key $SSH_KEY_ID
# Scripted: pick the shape yourself
orl deploy trainer --ssh-key $SSH_KEY_ID --gpu-model "h100 sxm" --gpu-count 2 --disk-gb 200
# Wait for it to boot, then open an SSH session
orl deploy trainer --ssh-key $SSH_KEY_ID --gpu-model "h100 pcie" --gpu-count 1 --connect
# Print the request without creating anything
orl deploy trainer --ssh-key $SSH_KEY_ID --gpu-model "h100 pcie" --gpu-count 1 --dry-run| Flag | What it does |
|---|---|
--gpu-model, --gpu-count | GPU model id or name and count (omit --gpu-model in a terminal to choose) |
--disk-gb, --image | Disk size and container image |
--ssh-key | Attach an org SSH key by id (repeatable, at least one required) |
--wait | Wait until the VM is ready, then print it |
--connect | Wait, then open an SSH session |
--dry-run | Print the request body and exit |
--yes | Skip the confirmation prompt |
Connect and manage
orl vms list # your VMs
orl vms ssh <vm-id> # SSH over the data plane
orl vms ssh <vm-id> --print # print the ssh command instead of running it
orl vms stop <vm-id> # stop the meter, keep the disk
orl vms terminate <vm-id> # destroy it (asks first; --force to skip)Watch spend
orl balance # balance and current-month spend
orl catalog pricing get # live GPU and CPU rates
orl usage current get # current-month usage breakdownScripting
orl is built for pipelines. Every command takes global flags:
--jsonprints the raw API response so you can pipe it tojq.--quietsuppresses progress and advisory notes.--no-colordisables ANSI color (also honored viaNO_COLOR).--no-input(orORL_NO_INPUT=1) never prompts: a step that would ask fails at once and names the flag that confirms, such as--force. orl 0.7.0 and later.
# The id of your most recent VM
orl vms list --json | jq -r '.items[0].id'From orl 0.7.0, OPENRELAY_API_KEY alone is enough: when no --org, OPENRELAY_ORG_ID or stored
login names an org, orl reads it from the key. Reads (GET) retry on 429, 5xx and dropped
connections, honoring Retry-After. A write is retried only when it carries an idempotency key you
gave it (below); orl never makes one up.
Repeating a create
A create that timed out may have gone through, so a plain create is sent once. Give it an idempotency key, a value you choose per create:
orl vms create --name box --gpu-model-id rtx-4090 --ssh-key-ids key_2f9c1a --idempotency-key box-2026-10-15 --jsonThe API runs a create once per key, so orl repeats a keyed create by itself on 429, 5xx and
dropped connections, as it does a read, and a second after an IDEMPOTENCY_KEY_IN_USE (the first
request with the key is still running). A capacity refusal such as INSUFFICIENT_GPU_CAPACITY is not
repeated: nothing frees up in a second. A keyed create that still fails reports retryable: true:
run the same command, with the same key and flags, again. The API answers a repeat with the first
create's result instead of making a second VM, and orl notes the replay on stderr. The same key with
other flags fails with IDEMPOTENCY_KEY_REUSED. With a key, orl deploy sends the create even when
the GPUs it asks for no longer show as free, and notes that on stderr: the first create under the
key may be what took them.
orl vms create, orl pods create, orl deploy and orl batches create take --idempotency-key
(the vms_create and batches_create MCP tools an idempotency_key property), and a result is kept
for 24 hours; the rules are in Idempotent creates. orl batches create has taken the flag since 0.7.0, when it only tagged the batch so a retry could find it; from
0.8.0 it also sends the key, and the other commands take the flag.
Errors as JSON
Set ORL_ERROR_FORMAT=json (orl 0.7.0 and later) and each failure is one JSON object on stderr,
with nothing but JSON on stderr (ORL_DEBUG=1 adds one orl.debug/v1 object per request):
{"schema_version":"orl.error/v1","error":{"code":"insufficient_balance","message":"insufficient balance; add funds to continue (insufficient_balance)","hint":"add funds or turn on auto-recharge on the dashboard's Usage page (https://app.openrelay.inc/usage); an API key cannot add funds","exit_code":4,"http_status":402,"request_id":"c40275d778c6e44a","retryable":false,"docs_url":"https://docs.openrelay.inc/docs/inference/errors"}}code is the API's own code when it sent one, otherwise one of orl's (NO_API_KEY,
INPUT_REQUIRED, NETWORK_ERROR, COST_CAP_EXCEEDED, WAIT_TIMEOUT, BATCH_FAILED, ...).
retryable says whether repeating the same request unchanged is safe and can succeed. Without the
variable, errors print as text, with the request id on its own line.
Exit codes
orl returns stable exit codes so scripts can branch on the failure kind. Run
orl exit-codes for the full list.
| Code | Meaning |
|---|---|
0 | success |
1 | unexpected or network error |
2 | usage error, a prompt under --no-input, or a 400 from the API |
3 | authentication failed (401): run orl auth login |
4 | forbidden or refused (402, 403): the key lacks a permission or its spend budget is used up (key_budget_exceeded), the org is wrong, or the account refuses the action (the codes in Account and balance refusals); the error code names the refusal |
5 | not found (404) |
6 | conflict (409), or the resource is not in the state the command needs yet |
7 | server error (5xx) |
8 | refused before spending: the cost estimate exceeds --max-cost or ORL_MAX_COST_USD (0.7.0) |
9 | stopped waiting at --timeout; the work continues, run the wait again (0.7.0) |
10 | orl auth status only, a warning: the key still works but expires within 7 days (0.8.0) |
11 | orl auth status only: the key has expired; other commands exit 3 (0.8.0) |
12 | orl auth status only: the key was revoked; other commands exit 3 (0.8.0) |
13 | the job finished unsuccessfully: a batch failed, expired or was cancelled (0.7.0) |
So a CI job can check its key before a long run: fail on 3, 11 and 12, and treat 10 as a
warning, since a key from an organization that caps key lifetime at 7 days or less reads as expiring
from the moment it is created. Before 0.8.0 orl auth status exited 0 for an expiring key and 3
for an expired or revoked one. Before 0.7.0 a 402 exited 1, and a missing required flag or an
unknown command exited 1 instead of 2.
Discover any command's flags with orl <command> --help, for example
orl deploy --help or orl vms --help.
Authentication
How orl signs you in (a browser device login, or an API key for scripts and CI), picks your organization, and which setting wins when several are set.
Kubernetes
orl kube manages Managed Kubernetes clusters, node pools and nodes, and writes kubeconfigs that authenticate kubectl with your OpenRelay API key.