# The szenera command line

`szenera` chooses the shape of its output itself, separates the kinds of error through exit codes, and bundles environment and credential in a profile. There is no ready-made package to download today. How to get the tool is described at `https://developer.szenera.com/cli`, together with the detailed reference for signing in, every command with all of its flags, JSON output, and exit codes.

> **Type:** reference
> **For:** Anyone who operates Szenera from scripts or wants to look something up quickly without opening the portal.
> **Prerequisite:** For most commands, an API key of your organization or an account of your organization including a second factor. `szenera health` needs neither.

## The principle: one output for people, one for processes

`szenera` decides the shape itself. If standard output is a terminal, you get a table with a header row above it. If the output goes to a file or a pipe instead, you get JSON. You can force either one with `--table` and `--json`.

In JSON mode, standard output carries the result and nothing else: no envelope, no status field, no timing. A script therefore reads `.items[]` and not `.daten.items[]`. Errors go to standard error without exception, so a redirected output never leaves behind a file that looks like a result.

## The most important commands

You need these commands most often. `szenera --help` shows the complete set.

| Command | What it does |
|---|---|
| `szenera health` | Reports whether the environment answers as ready. Needs no key. |
| `szenera whoami` | Tells you who your own credential is. |
| `szenera device list` | The devices of the organization. |
| `szenera location list` | The locations, with the number of displays attached to each. |
| `szenera hygiene get` | Counts what has been left lying around: unused media, scenes without an assignment, displays without a program or silent for a long time, dormant accounts, dead event targets. |
| `szenera api get <path>` | Reads any path of the API. |
| `szenera api post\|put\|patch\|delete <path> [body]` | The escape hatch to every write path. The body is given as JSON directly in the call or as `@datei.json`. |

`szenera <befehl> --help` shows a single command with its positional arguments, flags, and examples. The help is generated from the same description that the commands themselves come from, so a help that says something different from the tool cannot exist here.

A flag always carries its value after the equals sign: `--limit=20`, not `--limit 20`.

Every modifying call carries a fresh idempotency key by itself. You can set it yourself only with the raw call: `szenera api post <path> <body> --idempotency-key=<schluessel>`. You need this to resume an interrupted retry without the operation happening a second time.

## Exit codes

The codes separate the kinds of error so that a script can react without comparing text. In JSON mode, the kind also appears as a word in the error output, under `error.kind`.

| Code | Kind | Meaning |
|---|---|---|
| `0` | `ok` | Everything is fine. |
| `1` | `usage` | The call was wrong: unknown command, missing argument, wrong flag, or a request that contradicts the contract. |
| `2` | `config` | The configuration does not hold up: profile missing or broken, credential missing, profile locked. |
| `3` | `auth` | Sign-in rejected. |
| `4` | `forbidden` | Permission missing. |
| `5` | `not-found` | Not found. |
| `6` | `conflict` | Conflict. |
| `7` | `validation` | The input was rejected. |
| `8` | `rate-limited` | Too many calls in a short time. |
| `9` | `server` | Server error, or a response that contradicts the contract. |
| `10` | `network` | No response at all. |
| `11` | `failed` | The call went through and the operation ended in failure. |
| `12` | `timeout` | Time limit reached, the outcome is unknown. |

The last two are deliberately not `9`. Both went through, and anyone who treats a time limit like a failure triggers an operation that is already running a second time.

If a **response** breaks the contract, the usual reason is a version mismatch: the other side is older or newer than the tool asking it. The message says so and names the fields, and `--raw` additionally puts the unchanged response on standard output.

## Profiles

A profile bundles two things: the credential and the mapping from service to base URL. The profile file is located at `~/.config/szenera/profile.json`. The credential is usually an API key, which does not expire after minutes and therefore needs no session upkeep. If you only want to take a quick look, sign in with password and second factor instead. A profile can be write-protected (`readOnly`) — every modifying command then aborts instead of relying on your caution.

Instead of a single address, a profile carries a mapping. As long as everything is on the same server, the entry `*` is enough. If a service moves later, it is added with its own address, and no script changes.

Environment variables take precedence over the file, so a run can manage without a file and no secret has to be written to disk:

| Variable | Effect |
|---|---|
| `SZENERA_PROFILE` | Which profile applies. `--profile=` overrides it. |
| `SZENERA_BASE_URL` | Sets the `*` entry and thus carries an entire profile without a file. |
| `SZENERA_API_KEY` | The API key. It replaces the profile’s credential. |
| `SZENERA_OUTPUT` | `json` or `table`. `--json` and `--table` override it. |
| `SZENERA_PROFILE_FILE` | Where the profile file is located. |

An account that belongs to several organizations aborts the sign-in instead of guessing. A key never raises this question: it belongs to exactly one organization.

## Related

- [Create, rotate, and revoke an API key](/docs/developers/create-api-key)
- [What can your own programs do with Szenera?](/docs/developers/about-integrations)
