
# Command line interface

The `keenauth` command line interface (CLI) calls the management API from a terminal or a script. Every command wraps one typed method of the Go SDK, so the CLI and the API behave the same way. There is no installation package yet.

## Authentication and target

The CLI reads its credentials from the environment only. The API key is never accepted as a flag, so it does not end up in your shell history.

| Variable | Required | Description |
|---|---|---|
| `KEENAUTH_API_KEY` | yes | Management API key. If it is missing, the command exits with code 3. |
| `KEENAUTH_BASE_URL` | yes, unless `--base-url` is given | Management API base URL of your environment. |

Instead of an API key you can sign in with your dashboard account: `keenauth login`, then `keenauth env use <environment_id>`. The session is used only when `KEENAUTH_API_KEY` is not set, and the environment address then comes from the control plane (see [Signing in with keenauth login](/docs/cli/login)).

The `--base-url` flag overrides `KEENAUTH_BASE_URL` for one command. The CLI never prints the API key; if a server response echoes it, it is replaced with `[redacted]`.

## Commands

Commands follow the pattern `keenauth <group> <action> [arguments] [flags]`. Run `keenauth --help` or `keenauth <group> --help` for the full list.

| Group | Actions |
|---|---|
| `users` | `list`, `search`, `get`, `create`, `update`, `delete`, `block`, `unblock`, `cancel-deletion`, `authenticators list`, `authenticators delete`, `authenticators reset`, `authenticators reactivate`, `send-password-reset`, `send-verification-email`, `emails send-verification`, `start-recovery`, `logs list` |
| `applications` | `list`, `create`, `get`, `update`, `delete`, `get-api-grant`, `put-api-grant`, `delete-api-grant`, `credentials list`, `credentials create`, `credentials delete` |
| `apis` | `list`, `create`, `get`, `update`, `delete` |
| `connections` | `list`, `create`, `get`, `update`, `delete`, `activate`, `deactivate` |
| `identity-settings` | `get`, `update` |
| `environment-settings` | `get`, `update` |
| `config` | `export` (without `-o` it prints YAML), `plan -f <file>` (prints a readable diff in a terminal), `plans get`, `apply <plan_id>` (asks for confirmation; `--secrets-from-env` reads secrets from environment variables, `--confirm-deletion <kind>/<key>`), `promote --from <env> --to <env>` (exports, plans, shows the diff and applies after confirmation; see [Promoting configuration with keenauth config promote](/docs/cli/config-promote)) |
| `api-keys` | `list`, `get`, `create`, `rotate`, `revoke` |
| `logs` | `list`, `get`, `tail` (follows new records; `--type`, `--user`, `--org`; see [Following logs with keenauth logs tail](/docs/cli/tail)) |
| `support-access-grants` | `list`, `get`, `create`, `delete` (see [Giving support access with keenauth support-access-grants](/docs/cli/support-access-grants)) |
| `directories` | `simulate <directory_id> --scenario <name>` (runs a SCIM simulator scenario in development and staging; see [Testing SCIM provisioning with keenauth directories simulate](/docs/cli/directories-simulate)) |
| `audit` | `verify` (checks a downloaded audit export offline; see [Verifying audit exports with keenauth audit verify](/docs/cli/audit-verify)) |
| `branding` | `get`, `update`, `applications get`, `applications put`, `organizations get`, `organizations put`, `css put`, `page-template put`, `publish`, `rollback`, `versions list`, `texts get`, `texts put`, `texts delete` |
| `signing-keys` | `list`, `rotate`, `policy get`, `policy update` |
| `events` | `tail` (follows customer events through the event API; see [Following events with keenauth events tail](/docs/cli/events-tail)), `get <event ID>` (one event, exactly as the server sent it) |
| `mock` | `serve` runs a local OIDC mock server for offline tests; it needs no API key (see [Local mock server](/docs/cli/mock-server)) |
| `test` | `login --user <+test address>` signs a test user in to a `development` environment with the fixed code and prints the tokens; it needs no API key (see [Signing in test users with keenauth test login](/docs/cli/test-login)) |
| `webhooks` | `listen --forward-to <local URL>` forwards the events of a `development` or `staging` environment to your machine, signed with a temporary secret (see [Testing webhooks locally with keenauth webhooks listen](/docs/cli/webhooks-listen)) |
| `login`, `logout`, `whoami`, `env` | `login` (`--web`, `--insecure-storage`), `logout`, `whoami`, `env use <environment_id>`, `env list`: sign in with your dashboard account instead of an API key (see [Signing in with keenauth login](/docs/cli/login)) |

List commands accept `--limit`, `--after`, `--before` and `--order`. When more results exist, the next cursor is printed to stderr as `more results: --after <cursor>`.

## Output format

`--output` (`-o`) selects `table`, `json` or `yaml`. Without the flag the CLI prints a table on a terminal and JSON otherwise. JSON and YAML contain the API response unchanged and are never translated, so scripts can rely on them. Results go to stdout; errors, hints and the `etag` line go to stderr.

## Request bodies

Commands that send a body accept one flag per flat field, named after the JSON key with dashes (`phone_number` becomes `--phone-number`). Nested objects are given with `--data`:

- `--data @file.json` reads the body from a file.
- `--data -` reads the body from standard input.

Flags override the fields of the file. A body is limited to 4 MiB.

```sh
keenauth users create --email ada@example.com
keenauth apis create --data @api.json
echo '{"name":"Orders"}' | keenauth apis update api_123 --data - --if-match '"v3"'
```

## Optimistic concurrency: `--if-match` and the etag

Update and delete commands for applications, APIs and connections require `--if-match`; `users update` accepts it. The value is the `ETag` of the version you want to change. The server rejects the request if the resource changed in the meantime. `identity-settings update` requires it too. `environment-settings update` requires it too. Branding draft writes (`branding update`, `branding applications put`, `branding organizations put`, `branding css put`, `branding page-template put`) require it too.

Each `get` command (`users`, `applications`, `apis`, `connections`, `identity-settings`, `environment-settings`) prints the response `ETag` to stderr, so stdout stays clean:

```sh
$ keenauth apis get api_123
{ ... }
etag: "v3"
$ keenauth apis update api_123 --name Orders --if-match '"v3"'
```

The `etag` line is not added to the JSON or YAML output.

## Destructive commands and `--yes`

Commands that delete or block ask for confirmation (`[y/N]`) on a terminal. Declining exits with code 1 and prints `aborted`. When standard input is not a terminal, the command refuses to run unless you pass `--yes` (`-y`); it exits with code 2 before sending any request.

## Exit codes

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | Server or operational error, a declined confirmation, or a network failure. |
| `2` | Usage error: unknown command or flag, a missing argument, an invalid body, or `--yes` missing on a non-interactive terminal. |
| `3` | Authentication error: the key is missing, or the server answered 401 or 403. |
| `4` | The plan does not include the feature (`403` with `plan_feature_unavailable`). |

For server errors the CLI prints `error`, `code`, `status`, `detail`, `request_id` and one `field` line per validation error to stderr. Quote the `request_id` when you contact support.

## Language

Human-readable text (help, prompts, errors, table headers) is available in Turkish (`tr`), English (`en`), French (`fr`), German (`de`), Korean (`ko`), Japanese (`ja`), Italian (`it`) and Spanish (`es`). The language is taken from the first set of these variables: `KEENAUTH_LANG`, `LC_ALL`, `LC_MESSAGES`, `LANG` (for example `tr_TR.UTF-8` selects `tr`). An empty or unsupported value selects English, and a message missing in a language falls back to English.

Machine-readable output is never translated: JSON and YAML field names and values, the problem `code` and the `code`, `status` and `request_id` labels stay the same in every language. Messages that come from the server are shown as the server sent them.

```sh
KEENAUTH_LANG=de keenauth users list --help
```

## Application credentials

`keenauth applications credentials list|create|delete` manages client secrets and registered public keys of an application. These commands do not use `--if-match`.

```sh
keenauth applications credentials create app_123 --type client-secret --expires-at 2027-01-01T00:00:00Z
keenauth applications credentials create app_123 --type public-key --jwk-file key.pub.jwk
keenauth applications credentials delete app_123 cred_456 --yes
```

A client secret is printed once, to stdout, together with a one-time warning on stderr. `--jwk-file` takes a single public JWK (at most 16 KiB); a file with private members is refused before any request is sent.

## API keys

`keenauth api-keys list|get|create|rotate|revoke` manages the management API keys of the environment. These commands do not use `--if-match`.

```sh
keenauth api-keys create --name ci --type restricted --permissions users:read,users:write --expires-at 2027-10-01T00:00:00Z
keenauth api-keys rotate key_123 --overlap-hours 24
keenauth api-keys revoke key_123 --reason leaked --yes
```

The raw key of `create` and `rotate` is printed once, to stdout, together with a one-time warning on stderr; it cannot be retrieved again. `list` and `get` print metadata only.
