HTTP API
Find the running server, authenticate, then list runs and projects and start runs from a script
Call Orbital’s HTTP API when a script needs to list runs and projects or start a run. The running server answers under /api, on the same port as the browser app:
record=~/.orbital/server.jsoncurl --silent --show-error \ --header "Authorization: Bearer $(jq -r '.secret // ""' "$record")" \ "$(jq -r .url "$record")api/runs" \ || echo "No Orbital server is running." >&2For a coding agent, the MCP endpoint is usually the better choice. Its tools describe themselves and return what an agent needs.
Find the server
Section titled “Find the server”A running server records where it listens in ~/.orbital/server.json. This is true of orbital serve and of the server the desktop app or orbital app owns.
| Field | Holds |
|---|---|
url |
The server’s address. |
secret |
Only for a server a window owns. Every request to that server must carry it. |
Only you can read the file. The server removes it when it stops. A record whose URL does not answer is out of date; start Orbital again to replace it.
Authentication
Section titled “Authentication”| Server | What a request needs |
|---|---|
orbital serve |
No secret. A request without an Authorization header is fine. |
A window’s server, from the desktop app or orbital app |
Authorization: Bearer <secret>, with the secret from ~/.orbital/server.json. Without it, the server answers 401. |
The examples read the address and the secret with jq. .secret // "" gives an empty secret for orbital serve, which it accepts.
The MCP endpoint at /mcp is different. It always needs its own bearer token, even on orbital serve. See the endpoint.
Every server also applies these rules:
- A server bound to a loopback address answers only to the host names
localhost,127.0.0.1and[::1]. Other host names get403. - A server bound to a network address with
--bindanswers to any host name. Protecting that network is up to you. - A change request that another web site sends gets
403.
List runs
Section titled “List runs”Lists every run the server knows.
GET /api/runsIt takes no parameters.
It returns an array of runs. Each run has its runId, workflowName, goal, project and phase, among other fields. The phase field explains phase.
Example, with the response trimmed to a few fields:
curl --header "Authorization: Bearer $secret" "${url}api/runs"[ { "runId": "2026-09-24-npn6fk", "workflowName": "implement-ticket", "goal": "https://linear.app/acme/issue/IE-3770", "project": "proj_r3tQMqBPBB5p", "phase": "Executing" }]Related: Runs, Watch a run.
Get a run
Section titled “Get a run”Reads one run, in the same shape as an item of List runs.
GET /api/runs/{runId}| Name | Type | Required | Default | Description |
|---|---|---|---|---|
runId |
string, in the path | Required | The run id. |
It returns the run. A run the server does not know answers 404.
Example:
curl --header "Authorization: Bearer $secret" "${url}api/runs/2026-09-24-npn6fk"{ "runId": "2026-09-24-npn6fk", "workflowName": "implement-ticket", "phase": "Ended", "outcome": "success"}Related: Watch a run.
Start a run
Section titled “Start a run”Starts a run in a project, as orbital run start does.
POST /api/runsThe body is a JSON object:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
project |
string | Required | The project id. List projects gives it. | |
goal |
string | Required, unless you give ticket |
The run’s goal. | |
workflow |
string | Optional | The project’s default workflow | The workflow name. |
inputs |
object of strings | Optional | The workflow’s inputs, such as {"ticket_url": "<ticket URL>"}. The shipped ticket and epic workflows take ticket_url. |
|
title |
string | Optional | A generated title | One line of up to 80 characters that the run is listed under. |
harness |
string | Optional | The workflow’s choice | claude, codex or opencode. |
model |
string | Optional | A model for that harness. Only with harness. |
|
start |
boolean | Optional | true |
false records the run without starting it. |
ticket |
string | Optional | Older; use goal and inputs instead. See below. |
ticket takes a ticket’s URL or identifier. It becomes the goal when goal is missing, and it is passed as a workflow input named ticket. A workflow that declares no ticket input refuses it with a 400 that says the input ticket is not declared by the workflow. It is not the same as ticket_url, which is a workflow input you send inside inputs.
| Status | Meaning |
|---|---|
200 |
The run was created. The body is {"runId": "..."}. |
400 |
The server cannot take the request. The body’s detail names the problem, such as a missing input or an unknown workflow. |
401 |
The secret is missing or wrong. |
404 |
The project does not exist. |
The run queues like any other run. It starts when a slot is free, under the cap in Settings > General.
Example, starting a run from a ticket URL:
record=~/.orbital/server.jsonproject=proj_r3tQMqBPBB5pticket=https://linear.app/acme/issue/IE-3770curl --silent --show-error --fail-with-body \ --header "Authorization: Bearer $(jq -r '.secret // ""' "$record")" \ --header "Content-Type: application/json" \ --data "$(jq -n --arg project "$project" --arg ticket "$ticket" \ '{project: $project, workflow: "implement-ticket", goal: $ticket, inputs: {ticket_url: $ticket}, title: "Pay invoices from the portal"}')" \ "$(jq -r .url "$record")api/runs"{ "runId": "2026-09-24-npn6fk" }Related: Start a run, The run queue.
Projects
Section titled “Projects”List projects
Section titled “List projects”Lists the projects the server keeps. A project’s id is what Start a run takes as project.
GET /api/projectsIt takes no parameters.
It returns an array of projects. Each has an id, a name and its folders, each with a name and a path.
Example, printing each project’s id and name:
record=~/.orbital/server.jsoncurl --silent --show-error \ --header "Authorization: Bearer $(jq -r '.secret // ""' "$record")" \ "$(jq -r .url "$record")api/projects" | jq -r '.[] | "\(.id) \(.name)"'proj_r3tQMqBPBB5p acme-portalRelated: Projects and folders, Create a project.
Health
Section titled “Health”Check the server
Section titled “Check the server”Reports the server’s version, whether a newer release is available, and how many projects and runs it holds.
GET /api/healthIt takes no parameters.
It returns version, available, projects and runs.
Example:
curl --header "Authorization: Bearer $secret" "${url}api/health"{ "projects": 3, "runs": 41, "version": "0.3.0", "available": { "_tag": "UpToDate", "running": "0.3.0" } }Related: Install Orbital, Troubleshooting.
Other routes
Section titled “Other routes”The browser app uses the same API. These are the other groups a script is most likely to need. All paths start with /api.
| Group | Main routes | What it covers |
|---|---|---|
| Runs | /api/runs/{runId} and below |
Controls, messages and exports runs, and reads their thread, history and logs. |
| Workflows | /api/workflows?project=<id>, /api/workflows/{name} and below |
Lists the workflows a project offers, and reads, validates and edits workflows. |
| Projects | /api/projects/{projectId} and below |
Creates and changes projects and their folders. |
| Settings | /api/settings, /api/settings/changes, /api/settings/mcp |
Reads and changes settings, lists the recent changes, and reads the MCP connection. A change request may name its sender with the orbital-setting-source header; one that names none is recorded as a request that did not say who sent it. |
| Skills | /api/skills, /api/skills/install |
Lists the bundled skills and installs them. |
| Harnesses | /api/harnesses, /api/harnesses/check |
Reads and checks whether each harness is ready. |
The API has other groups the browser app uses, such as chats, triggers and personas.
Related
Section titled “Related”- MCP tools: the better choice for a coding agent, with self-describing tools
- Command line: start a run and change settings from a terminal
- Runs: what a run’s
phasemeans - Start a run: every way to start a run