Access & the raw API
Log in, switch between instances with contexts, manage personal access tokens, and call any API route directly with openship api.
These commands are how the CLI knows which Openship it's talking to and who you are — plus an escape
hatch (openship api) for reaching any route that doesn't have its own command yet.
Where auth lives
The CLI keeps its settings in ~/.openship/config.json (mode 0600). That file holds one or more named
contexts — each pins an API URL, a dashboard URL, and a personal access token (a opsh_pat_… string).
One context is active at a time; every authenticated command reads from it. The default context is called
default and points at your local instance (http://localhost:4000).
JSON output
--json is a global flag — put it before the subcommand (openship --json token list). In JSON mode the
data is written to stdout as JSON while status messages go to stderr, so a piped stream stays clean. Setting
OPENSHIP_JSON=1 in the environment does the same thing. openship api always prints the response body.
openship login
Authenticate against an instance and save the token into a context. Run with no --token and it opens the
dashboard's Settings page — where Personal Access Tokens live — and waits for you to paste one; pass
--token to log in non-interactively (CI, scripts). The token is validated against the API before it's stored, and it must
start with opsh_pat_.
# Interactive — opens the dashboard, prompts for a pasted token
openship login
# Non-interactive — against a remote self-hosted instance, stored as a named context
openship login \
--token opsh_pat_xxxxxxxx \
--api-url https://openship.example.com \
--dashboard-url https://openship.example.com \
--context prod| Flag | Purpose | Default |
|---|---|---|
--token <token> | Personal access token (opsh_pat_…) for non-interactive login. | (prompted) |
--api-url <url> | API base URL to authenticate against. | http://localhost:4000 |
--dashboard-url <url> | Dashboard base URL (used to open the token page). | http://localhost:3001 |
--context <name> | Name of the context to store this login under. | default |
Can't reach the API?
If login reports it couldn't reach the API, the host in --api-url isn't responding. Point it at the right
instance — for a remote self-hosted box that's the same URL you open in the browser.
openship logout
Remove the stored token from a context. The endpoints stay saved, so a later login just re-adds the token.
openship logout # active context
openship logout --context prod # a specific one| Flag | Purpose | Default |
|---|---|---|
--context <name> | Log out of a specific context. | (active context) |
openship context
Manage the named connections in ~/.openship/config.json. This is pure local config editing — no API calls.
Use it to keep several instances side by side (local, staging, production) and switch between them. Alias:
openship ctx. Running it bare prints the active context and the full list (the active row is starred).
openship context # show active + list all
openship context use prod # switch the active context| Subcommand | Purpose |
|---|---|
list (alias ls) | List configured contexts. |
use <name> | Switch the active context. |
add <name> | Create or update a context's endpoints/token. |
rm <name> (alias remove) | Remove a context (can't remove the active one — switch away first). |
The list shows one row per context: current (* marks the active one), name, apiUrl,
dashboardUrl, and auth (token when a token is stored, - when not).
context add
Register an instance without logging in interactively. add only writes config — use login when you want
the token checked against the API first.
openship context add staging \
--api-url https://staging.example.com \
--dashboard-url https://staging.example.com \
--token opsh_pat_xxxxxxxx \
--use| Flag | Purpose |
|---|---|
--api-url <url> | API base URL. |
--dashboard-url <url> | Dashboard base URL. |
--token <token> | Personal access token to store. |
--use | Switch to this context after adding. |
openship token
Create and manage the personal access tokens that authenticate the CLI and the API. Tokens are self-scoped — every call operates on your own tokens. In the dashboard these live under Settings → Personal Access Tokens.
| Subcommand | Purpose |
|---|---|
list | List your personal access tokens. |
create <name> | Mint a new token (the secret is shown once). |
revoke <id> | Revoke one of your tokens. |
token create
# A full-access, non-expiring token
openship token create "laptop"
# A read-only token that expires in 30 days
openship token create "ci-reader" --read-only --expires 30
# Scope a token to one project with specific permissions (repeatable)
openship token create "deploy-bot" --grant project:abc123:read,write| Flag | Purpose | Default |
|---|---|---|
--read-only | Reject mutation methods (POST/PUT/PATCH/DELETE). | off |
--expires <days> | Expire after N days (1–365); omit for a non-expiring token. | never |
--grant <type:id:perms> | Scope the token to a resource. Repeatable. Format type:id:perm1,perm2, e.g. project:abc123:read,write. | (full access) |
Copy the secret immediately
token create prints the full opsh_pat_… secret once and never again. token list only ever shows a
short prefix. If you lose it, revoke the token and mint a new one.
The list table shows: id, name, prefix, readOnly, scoped, expires, lastUsed, and revoked.
Pass a token's id (they look like pat_…) to revoke:
openship token list
openship token revoke pat_abc123openship api
The escape hatch. Make an authenticated request to any API route using the active context's token —
much like gh api. This is what keeps the CLI small: anything without a dedicated command (audit log,
one-off reads, routes added after your CLI version) is still reachable here.
The <path> argument is relative to /api, so openship api /projects calls
<your-api-url>/api/projects. The method defaults to GET, or POST when you pass --data.
# GET a collection
openship api /projects
# GET a single resource
openship api /deployments/dep_abc123
# POST with a JSON body (method defaults to POST when --data is given)
openship api /domains -d '{"projectId":"proj_123","hostname":"app.example.com"}'
# DELETE with an explicit method
openship api /tokens/pat_abc123 -X DELETE
# Add query parameters (repeatable)
openship api /domains -q projectId=proj_123| Flag | Purpose | Default |
|---|---|---|
-X, --method <method> | HTTP method. | GET, or POST when --data is set |
-d, --data <json> | Request body as a JSON string. | (none) |
-q, --query <kv...> | Query parameter key=value. Repeatable. | (none) |
The response body is printed to stdout — pretty-printed when it's JSON, raw otherwise. On a non-2xx
response the body is still printed and the command exits non-zero, so you can pipe it into jq and still
detect failures in scripts.
Which routes exist?
Browse the API reference for paths, methods, and required permissions. The resource commands
(project, service, domain, and friends) wrap the common ones in a friendlier
form — reach for openship api when there's no dedicated command.