Skip to content
OpenRelay is in early access, and the /v1 API is stable. New capabilities ship in the changelog.
CLI

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
FlagWhat it does
--gpu-model, --gpu-countGPU model id or name and count (omit --gpu-model in a terminal to choose)
--disk-gb, --imageDisk size and container image
--ssh-keyAttach an org SSH key by id (repeatable, at least one required)
--waitWait until the VM is ready, then print it
--connectWait, then open an SSH session
--dry-runPrint the request body and exit
--yesSkip 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 breakdown

Scripting

orl is built for pipelines. Every command takes global flags:

  • --json prints the raw API response so you can pipe it to jq.
  • --quiet suppresses progress and advisory notes.
  • --no-color disables ANSI color (also honored via NO_COLOR).
  • --no-input (or ORL_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 --json

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

CodeMeaning
0success
1unexpected or network error
2usage error, a prompt under --no-input, or a 400 from the API
3authentication failed (401): run orl auth login
4forbidden 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
5not found (404)
6conflict (409), or the resource is not in the state the command needs yet
7server error (5xx)
8refused before spending: the cost estimate exceeds --max-cost or ORL_MAX_COST_USD (0.7.0)
9stopped waiting at --timeout; the work continues, run the wait again (0.7.0)
10orl auth status only, a warning: the key still works but expires within 7 days (0.8.0)
11orl auth status only: the key has expired; other commands exit 3 (0.8.0)
12orl auth status only: the key was revoked; other commands exit 3 (0.8.0)
13the 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.

On this page