Skip to content
Anlyon
Esc
↑↓navigate↵open⌘Jpreview
On this page

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, requestId and hint are present when they apply.

  • Output is redacted. API keys, tokens and Bearer values 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 --timeout then 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-key is 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_KEY comes from a secret, never the repository. Create the key in the target environment with only the scopes the job needs (here actions:invoke and actions:read, because --wait reads the invocation while it waits). With an API key set, profiles are ignored.
  • --idempotency-key includes 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 production asserts the key is bound to production. If it is not, the command stops with exit code 10 before anything is sent.
  • --wait 15m follows an approval to its outcome. If nobody decides within 15 minutes, the command exits 12.
  • --yes confirms the write. Without it a non-interactive write is refused and nothing is sent. --no-input makes sure the CLI never waits on a prompt.
  • set +e stops 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 --json field. Each such change carries a changelog entry marked Breaking. From 1.0, those are major-version changes only.
  • Machine contract. Scripts should rely on --json output and exit codes, not on human-readable output, which may change in any release.
  • SDK. Each build bundles a specific @anlyonhq/sdk, shown by anlyon --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/identity and GET /api/v2/environment, and, for browser login, OAuth discovery at /.well-known/oauth-protected-resource/mcp.
  • Config. config.json carries a version. 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.

Was this page helpful?