Using the CLI in CI
Run restward non-interactively in CI pipelines - log in from secrets, isolate the config per job, parse --json output and rely on exit codes.
The CLI runs unattended. Outside a terminal (when stdin or stdout is redirected, as in every CI runner) it never prompts: anything it would ask for must come from flags or environment variables (or stdin), otherwise the command fails.
Authentication
Credentials come from your CI secret store through environment variables - never from the command line, where other processes on the runner could read them. There are two ways:
1. Log in with a password. Put the account's password in RESTWARD_PASSWORD (or pipe it
with --password-stdin) and log in at the start of the job:
export RESTWARD_API_URL=https://api.restward.tech
restward login --email "$RESTWARD_EMAIL" --password-stdin <<< "$RESTWARD_PASSWORD"
# or, with RESTWARD_PASSWORD set in the job's environment:
restward login --email "$RESTWARD_EMAIL"The session is stored in the config file and renews itself: the CLI swaps its refresh token for a new access token before the old one expires, so long jobs keep working (a refresh token lasts 7 days with the platform's default settings).
2. Use a token directly. Set RESTWARD_TOKEN to a bearer access token and every command
uses it, with no login and no config file:
export RESTWARD_API_URL=https://api.restward.tech
export RESTWARD_TOKEN=... # from your secret store
export RESTWARD_ORG=6f1c2a8e-... # optional: defaults to the token's organization
restward list --jsonA token set this way is never stored and never renewed: platform access tokens are
short-lived (15 minutes by default), so this fits short jobs or tokens minted by an earlier
step. Long-lived automation tokens (personal access / service tokens) are not available on the
platform yet. restward login --token stores such a token in the config file instead (read
from stdin or RESTWARD_TOKEN, never from the command line).
Precedence
For each value the CLI uses the first one that is set:
| Value | 1st | 2nd | 3rd |
|---|---|---|---|
| Access token | RESTWARD_TOKEN |
stored session | - |
| Base URL | RESTWARD_API_URL (or RESTWARD_BASE_URL) |
stored session | - |
| Organization | RESTWARD_ORG (a GUID) |
the token's organization (with RESTWARD_TOKEN) |
stored session |
Password (login) |
--password-stdin |
prompt (terminal only) | RESTWARD_PASSWORD |
--base-url on login wins over the environment. restward whoami shows which source is in
use (Authenticated via: RESTWARD_TOKEN or stored session).
A few things to know:
- Use a dedicated account for automation rather than a person's login.
- Environment variables are visible to the job's own processes. Prefer your CI system's masked secrets, and runners that are not shared with untrusted jobs.
restward logoutat the end of the job clears the stored session and revokes that session on the platform (only that one: other jobs and the dashboard stay signed in). If the platform cannot be reached the session is cleared locally and expires on its own;restward logout --everywherewould sign the account out everywhere.
Isolate the config per job
The CLI stores the session in a config file. Point RESTWARD_CONFIG at a job-local path so
parallel jobs on one runner do not share or overwrite each other's session, and remove it
when the job ends:
export RESTWARD_CONFIG="$RUNNER_TEMP/restward.json"
restward login ...
# ... your commands ...
restward logoutJSON output
Add --json to any command to get one JSON object on stdout:
{ "success": true, "value": { ... } }
{ "success": false, "error": "..." }Progress lines are suppressed in this mode and errors go to stdout as the envelope, so the output is always parseable:
endpoint=$(restward list --json | jq -r '.value.apis[] | select(.slug == "orders-db") | .endpoint')The value fields of each command are listed in the command reference.
Exit codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Failure: not logged in, no organization selected, nothing matching to act on, rejected credentials, network or platform errors |
2 |
Usage error: unknown command, unknown or invalid options, missing or extra arguments, an unknown subcommand, or unparsable input such as a malformed connection string or organization id |
A 2 means the command line itself is wrong, so retrying it will not help; a 1 can be transient
(network, platform) or a missing precondition (log in first).
Example: GitHub Actions
jobs:
provision:
runs-on: ubuntu-latest
env:
RESTWARD_API_URL: https://api.restward.tech
RESTWARD_CONFIG: ${{ runner.temp }}/restward.json
steps:
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- run: dotnet tool install -g restward
- name: Log in
run: restward login --email "$EMAIL" --json
env:
EMAIL: ${{ secrets.RESTWARD_EMAIL }}
RESTWARD_PASSWORD: ${{ secrets.RESTWARD_PASSWORD }}
- name: Connect the staging database
run: restward connect orders-staging --json > connect.json
env:
RESTWARD_CONNECTION_STRING: ${{ secrets.ORDERS_STAGING_DB }}
- name: Log out
if: always()
run: restward logout || true # exits 1 when there is no session (e.g. login failed)connect.json contains the new API key in plaintext (value.key.plaintext). Move it to
your secret store in the same job, and never upload that file as a build artifact or print
it to the log.