Scripting and CI

Run the BankSync CLI non-interactively in scripts and CI: API key auth, JSON output, stable exit codes, and machine-readable errors.

4 min read

On this page

The CLI is built to be scriptable first. In a non-interactive context it authenticates from an environment variable, emits JSON, never prompts, and reports failure through stable exit codes and machine-readable errors, so a script or pipeline can branch on results without parsing prose.

Authenticate without logging in#

In CI you do not run login. Provide the key through BANKSYNC_API_KEY and every command uses it:

Shell
export BANKSYNC_API_KEY="bsk_your_api_key_here"banksync banks list

Store the key as a secret in your CI provider, never in the repository. Use a least-privilege key: a reporting job needs only read scopes.

Output is JSON when piped#

Because the default format is JSON whenever stdout is not a terminal, scripts get structured output with no flag. You can still be explicit with --json:

Shell
banksync banks list | jq '.data | length'banksync tx list --bank amex --json > txns.json

The top-level shape is always { "data": ..., "meta": ... }, so .data is a reliable path. See output formats for the full envelope.

The CLI never blocks in CI#

When there is no terminal, the CLI behaves as if --no-input were set: it never prompts and never waits. A missing required argument becomes an error, not a hang. CI=true is treated as non-interactive too. Destructive commands refuse to run without --yes:

Shell
banksync feeds delete fed_123 --yes

Use --quiet to suppress spinners and notices if you want data only, though these already go to stderr and are auto-suppressed when piped.

Exit codes#

Every command exits with a stable code that classifies the outcome. Branch on these rather than parsing messages:

CodeMeaning
0Success
1Generic or unexpected error (including 5xx)
2Invalid usage or bad request (400)
3Unauthenticated or forbidden (401 or 403)
4Resource not found (404)
5Conflict, for example a sync already running (409)
6Semantic validation failed (422)
7Rate limited (429)
8Provider does not support this capability (501)
9Backend unavailable or consent required (503)

The same map is available at runtime from banksync commands --json under exit_codes, so an agent or script can read it without hard-coding.

Machine-readable errors#

In any non-table format (or when piped), an error is a single JSON object on stderr while stdout stays empty. The shape is stable:

JSON
{  "error": {    "code": "conflict",    "message": "A sync is already running for this feed.",    "status": 409,    "hint": "A conflicting operation is in progress. For syncs, pass --force.",    "retry_after": 60  }}

code is a stable snake_case identifier (never the raw API message), status is the HTTP status when there was one, hint is a short suggestion, and retry_after (seconds) appears on rate-limit errors. On a terminal you instead get a short human Error line with a dim hint.

Retries#

Idempotent GET requests retry automatically on rate limits (honoring Retry-After) and transient network errors, with backoff. Control it with --max-retries <n> or disable it with --no-retry. Non-GET requests are never retried.

A bash script#

This example reads data and fails cleanly on any error:

Shell
#!/usr/bin/env bashset -euo pipefail
export BANKSYNC_API_KEY="${BANKSYNC_API_KEY:?set BANKSYNC_API_KEY}"
# Fail the script if the CLI exits nonzero; capture JSON for processing.banks=$(banksync banks list --json)echo "$banks" | jq -r '.data[] | "\(.id)\t\(.name)\t\(.connectionStatus.status)"'
# Branch on a specific failure class using the exit code.if ! banksync feeds sync fed_123 --watch; then  code=$?  if [ "$code" -eq 5 ]; then    echo "A sync was already running; passing --force" >&2    banksync feeds sync fed_123 --force --watch  else    echo "Sync failed with exit code $code" >&2    exit "$code"  fifi

GitHub Actions#

Install the CLI, authenticate from a secret, and export a report artifact:

name: Nightly transactions exporton:  schedule:    - cron: "0 6 * * *"jobs:  export:    runs-on: ubuntu-latest    steps:      - name: Install the BankSync CLI        run: curl -fsSL https://banksync.io/install.sh | sh      - name: Export transactions        env:          BANKSYNC_API_KEY: ${{ secrets.BANKSYNC_API_KEY }}        run: |          banksync tx list --bank amex --all -o ndjson > transactions.ndjson      - uses: actions/upload-artifact@v4        with:          name: transactions          path: transactions.ndjson

Because the runner is non-interactive, the CLI emits NDJSON without a format flag; the explicit -o ndjson here makes the intent clear and stable.

Next steps#

  • For agents: the same guarantees, framed for AI agents.
  • Output formats: the JSON envelope and NDJSON streaming.
  • Telemetry: what is collected and how to opt out in CI.

Use this page with your AI assistant

Every BankSync doc is available as plain Markdown for agents and LLMs.