# Authentication

> How l4 resolves a credential on every command, and why browser login is not enough for the write commands.

Source: https://docs.levelfour.ai/cli/authentication


`l4` resolves credentials in a fixed order on every invocation:

1. The `--token` / `-t` flag
2. The `LEVELFOUR_TOKEN` environment variable
3. The OS keychain (populated by `l4 auth login`, interactive use only)

The first match wins. If none yields a token, the command exits with code `4` (`ExitAuthRequired`) and prints a hint.

## Interactive: browser login

On your own machine, sign in through the browser:

```bash
l4 auth login
```

This opens your default browser to the LevelFour sign-in page, completes the device-code exchange, and stores the resulting token in the OS keychain (macOS Keychain, Linux Secret Service via `libsecret`, Windows Credential Vault). Later commands pick it up from there, and you pass nothing.

To force a fresh session even if you are already logged in:

```bash
l4 auth login --force
```

To clear the stored token:

```bash
l4 auth logout
```

Confirm the login landed with `l4 whoami`. [Inspect the active session](#inspect-the-active-session) has what a good answer looks like.

## Browser login mints a read-only key

The key `l4 auth login` creates has the `read` scope. That covers the read commands: `costs`, `recommendations list`, `recommendations view`, `integrations`, `status`, `estimate`, `diff`, `export`, and `whoami`. `l4 api` reaches whatever the key's scope allows, so it works for reads and fails on any endpoint that requires `read-write`.

The write commands ([`rec accept`](/cli/commands/recommendations/accept), [`rec reject`](/cli/commands/recommendations/reject), [`rec execute`](/cli/commands/recommendations/execute)) change state on a recommendation, so they need a `read-write` key. Browser login will not give you one. Create it in the dashboard under **Settings > API Keys**, choose the **Read & write** scope, and pass it explicitly:

```bash
export LEVELFOUR_TOKEN="l4_live_your_read_write_key"
l4 rec accept REC-1234
```

> **Error**
>
> A `read` key that calls a write command fails with `403` and the hint `permission denied: this API key lacks permission for this operation`.

[Key scopes](/authentication#key-scopes) has the full read versus read-write split.

> **Tip**
>
> Keep the read-write key out of anything that only reads. Use a `read` key for monitoring, dashboards, and CI checks, and reserve the read-write key for the job that applies changes.

## CI / scripting: `LEVELFOUR_TOKEN`

For unattended environments, set `LEVELFOUR_TOKEN` from a secret:

```bash
export LEVELFOUR_TOKEN="l4_live_..."
l4 recommendations list --json
```

In GitHub Actions, as a step inside a job:

```yaml
- name: Run LevelFour CLI
  env:
    LEVELFOUR_TOKEN: ${{ secrets.LEVELFOUR_TOKEN }}
  run: l4 recommendations list --status available --jq '.data.data.items[] | .recommendation_id'
```

The token never touches the filesystem in this mode. [CI/CD integration](/guides/ci-cd) has complete workflows that wire the same secret.

## One-shot override: `--token`

For ad-hoc commands or running as a different identity:

```bash
l4 --token "l4_live_..." recommendations list
```

The flag wins over both the env var and the keychain. Reach for it when you hold several keys, service accounts among them, and do not want to run `l4 auth login` again.

## Inspect the active session

```bash
l4 whoami                  # identity, organization, role, connected accounts
l4 auth status             # which credential source is active
l4 auth status --verify    # additionally validate the token against the API
```

> **Success**
>
> A `whoami` that names your identity and organization is the proof the credential resolved.

`l4 whoami` prints the scope of the active credential, so you can tell a read key from a read-write one before running a write command.

> **Warning**
>
> `l4 auth status` names the credential source without calling the API. Pass `--verify` when you need to know the token still works, or the check passes on a revoked key and the script carries on.

## Creating and rotating keys

Create or rotate keys in **Settings > API Keys** in the dashboard. [API keys](/account/api-keys) walks through both. [Key formats](/authentication#key-formats) covers the `l4_live_` and `l4_test_` prefixes and which environment each one reaches.

## Switching API hosts

Point at a non-default API base URL with `--api` or `LEVELFOUR_API`:

```bash
l4 --api https://api.staging.levelfour.ai whoami
```

The same auth resolution rules apply. Only the base URL changes. To make one stick across commands, store it as the `api_base` key with [`l4 config`](/cli/commands/config).

## Troubleshooting

| Symptom                                                                          | Likely cause                                      | Fix                                                         |
| -------------------------------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------- |
| Exit `4`, "not authenticated"                                                    | No token resolvable                               | Run `l4 auth login` or set `LEVELFOUR_TOKEN`                |
| Exit `1`, "401 Unauthorized"                                                     | Token revoked or wrong environment (test vs live) | Rotate the key, or use `l4 --token ...` to test the new one |
| Exit `1`, "403 permission denied" on `rec accept` / `rec reject` / `rec execute` | The key has the `read` scope                      | Create a **Read & write** key in **Settings > API Keys**    |
| Browser does not open on `auth login`                                            | No `BROWSER` resolved                             | Open the printed URL manually; the CLI keeps polling        |
| Keychain access denied on macOS                                                  | Keychain locked                                   | Unlock the login keychain in **Keychain Access**            |

## Next

* [CLI overview](/cli) owns the exit codes and the full command catalogue
* [Installation](/cli/installation) if `l4` is not on this machine yet
* [CI/CD integration](/guides/ci-cd) has the GitHub Actions and GitLab CI workflows in full
* [Connecting a client](/mcp/authentication) takes these same keys, or signs you in instead
