Skip to content

Scripts and CI

Work without a profile file by using environment variables, evaluate exit codes, fetch lists completely, limit wait times, and make retries safe.

4 minute readCLIUpdated: 10/25/2026

On this page

Before you start

For
Anyone who runs the CLI in a CI pipeline or a cron job
Prerequisite
the built CLI available in the run, the base URL of the environment, and an API key ak_… with the permissions of the commands, stored as a CI secret and not as a file
Where
command line

After this guide, the CLI runs in a pipeline without a profile file. Your script reacts to exit codes instead of text, fetches lists completely, limits wait times, and retries calls that change something without triggering an operation twice.

Steps

  1. Store the address and key as secrets of your CI and pass them into the run as SZENERA_BASE_URL and SZENERA_API_KEY. Together, the two variables carry a complete profile. No file on disk is needed, and the profile is not write-protected.

  2. Call the CLI directly through the built file, never through pnpm run, or your script sees the code 1 for every error:

    export SZENERA_BASE_URL="https://<basis-adresse>"
    export SZENERA_API_KEY="$CI_GEHEIMNIS_SIGNAGE"
    szenera() { node packages/cli/dist/main.js "$@"; }
    
  3. At the start of every pipeline, check the environment and the credential with two calls that separate network from key:

    szenera health --json > /dev/null || { echo "Umgebung nicht bereit"; exit 1; }
    szenera whoami --json | jq -r '.tenantId'
    
  4. Evaluate the exit code, not the message. On failure, standard output carries nothing, so it can safely go into a file:

    szenera asset upload --file=plakat.png --json > ergebnis.json
    case $? in
      0)     echo "fertig: $(jq -r '.id' ergebnis.json)" ;;
      8|10)  sleep 30; echo "später erneut versuchen" ;;   # throttled or no network
      11)    echo "Vorgang endete rot — Befund lesen"; exit 1 ;;
      12)    echo "Zeitlimit — erst nachsehen, nicht blind wiederholen"; exit 1 ;;
      *)     exit 1 ;;
    esac
    
  5. Process the JSON output with jq. Lists carry items and, as long as pages follow, nextCursor:

    szenera device list --json | jq -r '.items[] | select(.status=="active") | .id'
    

Options

Fetch lists completely. --all follows all pages until there are no more and returns one concatenated list without nextCursor. For large collections, combine it with --limit=200, the cap per page:

szenera scene list --all --limit=200 --json | jq -r '.items[].id'

Limit wait times. Commands that wait for an operation (asset upload, asset reprocess, device screenshot, device send-command --wait) accept --timeout=<dauer> (90s, 20m, 2h, or a number in seconds) and some also --interval=<dauer>. --no-wait only triggers the operation and returns immediately, without knowing the outcome. Exit 12 always means that the outcome is unknown and the operation may still be running.

Make retries safe. For the escape hatch api post|put|patch|delete, the CLI generates a fresh idempotency key for every call. --idempotency-key=<schluessel> is needed only to continue an aborted retry, and then with the same value as on the first attempt. Commands that are idempotent by themselves say so in their help: sending player set-program twice is not a second operation.

szenera api post /v1/playlists @playlist.json --idempotency-key="import-${CI_LAUF_ID}"

Protection against irreversible actions. Commands that do something irreversible require --yes and abort with exit 1 without the flag: asset purge, asset delete, scene delete, api-key revoke, and more. szenera <befehl> --help names the flag. For profiles in a file, readOnly: true remains the lock: a command that changes something then aborts with exit 2 before a network packet is created. A profile made only of environment variables does not have this lock.

Check the result

  • szenera health and szenera whoami run at the start of the pipeline with exit 0. whoami names the expected organization under tenantId.
  • A deliberately wrong call (szenera device list --state=active) ends with exit 1 and a message on standard error. The redirected file stays empty.
  • After an upload, szenera asset get <ast_…> --json shows the state ready.

If something goes wrong

Symptom Meaning Solution
Every error ends with 1, even a 401 The call runs through pnpm run. Call the file directly through node (step 2).
Keine Konfiguration gefunden (no configuration found) (exit 2) SZENERA_BASE_URL is missing or empty. Set both variables in the run. See Profiles and credentials.
Exit 3 on every command except health The key is not valid: revoked, expired, or issued for another environment. Create a new key and replace the secret.
Exit 4 on a single command The key does not carry the permission, or the permission is not meant for keys. Check the “Key” column under Commands for your organization. Otherwise use a profile with password sign-in.
Exit 12 after an upload The time limit was too tight. Processing continues. Set --timeout=20m and check with asset get before a retry.