Scripting and CI
The anlyon CLI's machine contract: JSON output, stdout and stderr, exit codes, environment variables, a CI example, and what may change between releases.
Scripts, CI jobs and agents should rely on --json output and exit codes, not on human-readable output, which may change in any release. The flags and exit codes on this page are the machine contract. While the CLI is 0.x, a minor release changes one only with a changelog entry marked Breaking (see Compatibility).
Terminal contract
| Flag | Meaning |
|---|---|
--json |
stdout carries exactly one JSON document. Lists: {"data": [...], "nextCursor": string|null, "total"?: number}. Single objects: {"data": {...}}. |
--profile <name> |
Use this profile. |
--environment <id|slug> |
Assert the credential’s environment. It never switches it. |
--input <file|-> |
Read options as a JSON object (keys are the command’s option names, plus limit/cursor). Flags win. Unknown keys are rejected. |
--limit <1-100> / --cursor <c> |
Pagination for list commands. Pass back nextCursor. Cursors are opaque and bound to the list that issued them. |
--timeout <duration> |
Give up after e.g. 30s, 2m, 500ms (default 60s, 10m for auth login, and the wait plus one minute when --wait is set). |
--no-input |
Never prompt or open a browser. Fail instead. |
For actions invoke, --input supplies the action input itself, as an alternative to --data.
Output
-
Results go to stdout. Progress, hints, warnings and errors go to stderr.
-
With
--json, an error is one JSON object on stderr:{"error": {"code": "...", "message": "...", "exitCode": 4, "status": 403, "requestId": "...", "hint": "..."}}status,requestIdandhintare present when they apply. -
Output is redacted. API keys, tokens and
Bearervalues are removed before anything is printed. -
Ctrl-C cancels the in-flight request and exits 130. The exception is a write (
actions invoke,actions declare,approvals approve,approvals deny) that was sent and has no reply yet. Ctrl-C or--timeoutthen exits 13, because the request may have reached Anlyon. The error names the idempotency key to re-run with.
Paging through a list
anlyon invocations list --status unknown --limit 100 --json > page1.json
anlyon invocations list --status unknown --limit 100 --json \
--cursor "$(jq -r .nextCursor page1.json)" > page2.json
nextCursor is null on the last page.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (unexpected, or no more specific code) |
| 2 | Usage: bad flags, arguments or --input. Nothing was sent |
| 3 | Not logged in, or the credential expired, was revoked or was rejected (401) |
| 4 | Permission denied: missing scope or role (403) |
| 5 | Not found in this workspace/environment (404) |
| 6 | Rejected by the server as invalid or conflicting (400/409/422) |
| 7 | Rate limited or over quota (429) |
| 8 | Server error or unreachable (5xx, network) |
| 9 | --timeout elapsed. A write cut off before its reply exits 13 instead |
| 10 | Target mismatch: --environment, a changed binding, or an API origin override |
| 11 | Needs a server endpoint that is not available yet. Nothing was sent |
| 12 | Action accepted, not finished: waiting for approval or still running |
| 13 | Outcome unknown: the action may have taken effect, or a write was cut off before its reply. Do not retry with a new key |
| 14 | Action did not succeed: failed, denied, or its approval expired |
| 130 | Cancelled (Ctrl-C). A write cut off before its reply exits 13 instead |
anlyon doctor exits 1 when a check fails and 0 otherwise. A warning does not fail it. Exit 11 is reserved for a command whose endpoint a server does not have. No current command uses it.
The codes that need a decision from a script are 12, 13 and 14:
- 12: Nothing has failed. The action is waiting for a person, or still running. Check later with
anlyon invocations get <id>, or invoke with--wait <duration>. - 13: The destination may or may not have acted. Do not retry with a new idempotency key. Reconcile with the destination first. Re-running with the same
--idempotency-keyis safe: it returns the recorded result. - 14: The action did not run successfully: it failed, a reviewer or policy denied it, or its approval expired.
Environment variables
| Variable | Purpose |
|---|---|
ANLYON_PROFILE |
Profile for this shell. |
ANLYON_API_KEY |
API key for automation. It bypasses profiles. |
ANLYON_API_URL |
API origin for ANLYON_API_KEY and new logins (ANLYON_BASE_URL is accepted too). |
ANLYON_CONFIG_DIR |
Config directory. The default is $XDG_CONFIG_HOME/anlyon when XDG_CONFIG_HOME is set, ~/.config/anlyon otherwise, and %APPDATA%\anlyon on Windows. |
ANLYON_CREDENTIAL_STORE |
auto (default), keychain, or file (unencrypted opt-in). |
ANLYON_NO_BROWSER=1 |
Print the login URL instead of opening a browser. |
CI example
A GitHub Actions step that invokes an action with an API key stored as a repository secret, waits up to 15 minutes for an approval, and branches on the exit code:
- name: Refund via Anlyon
env:
ANLYON_API_KEY: ${{ secrets.ANLYON_API_KEY }}
run: |
npm install -g @anlyonhq/cli
set +e
anlyon actions invoke refund_payment \
--data '{"charge":"ch_123","amount":1200}' \
--idempotency-key "refund-ch_123-${{ github.run_id }}" \
--environment production \
--wait 15m --yes --no-input --json > result.json
code=$?
set -e
case "$code" in
0) echo "Refund succeeded." ;;
12) echo "::warning::Still waiting for approval $(jq -r .data.approvalId result.json)." ;;
13) echo "::error::Outcome unknown. Reconcile with the destination before re-running; do not change the idempotency key."; exit 1 ;;
14) echo "::error::Refund did not succeed: $(jq -r .data.status result.json)."; exit 1 ;;
*) exit "$code" ;;
esac
What each part does:
ANLYON_API_KEYcomes from a secret, never the repository. Create the key in the target environment with only the scopes the job needs (hereactions:invokeandactions:read, because--waitreads the invocation while it waits). With an API key set, profiles are ignored.--idempotency-keyincludes the workflow run id, so re-running a failed job reuses the key and returns the recorded result instead of invoking the action twice.--environment productionasserts the key is bound to production. If it is not, the command stops with exit code 10 before anything is sent.--wait 15mfollows an approval to its outcome. If nobody decides within 15 minutes, the command exits 12.--yesconfirms the write. Without it a non-interactive write is refused and nothing is sent.--no-inputmakes sure the CLI never waits on a prompt.set +estops the shell failing the step before the exit code can be read.
For a successful, pending or finished invoke, --json writes the invocation under data (with status and approvalId), and the idempotency key used under idempotencyKey.
Compatibility
- Versioning. The CLI is versioned independently of the SDK and the API. While 0.x, a minor release may add commands, flags, JSON fields and exit codes. A minor release may also remove or rename one, change the meaning of an exit code, or change the shape of an existing
--jsonfield. Each such change carries a changelog entry marked Breaking. From 1.0, those are major-version changes only. - Machine contract. Scripts should rely on
--jsonoutput and exit codes, not on human-readable output, which may change in any release. - SDK. Each build bundles a specific
@anlyonhq/sdk, shown byanlyon --version. The CLI’s tests run against that SDK’s source and check every request path against the API’s OpenAPI contract. - Server. The CLI requires
GET /api/v2/auth/identityandGET /api/v2/environment, and, for browser login, OAuth discovery at/.well-known/oauth-protected-resource/mcp. - Config.
config.jsoncarries aversion. A CLI refuses a config version it does not know rather than rewriting it. - Platforms. Linux (x64, arm64), macOS (x64, arm64) and Windows (x64). The npm package supports Node 20.3 and later.

