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:

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:

l4 auth login --force

To clear the stored token:

l4 auth logout

Confirm the login landed with l4 whoami. 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, rec reject, rec 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:

export LEVELFOUR_TOKEN="l4_live_your_read_write_key"
l4 rec accept REC-1234
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 has the full read versus read-write split.

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:

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

In GitHub Actions, as a step inside a job:

- 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 has complete workflows that wire the same secret.

One-shot override: --token

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

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

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
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.

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, or through the API Keys endpoints. 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:

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.

Troubleshooting

SymptomLikely causeFix
Exit 4, "not authenticated"No token resolvableRun 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 executeThe key has the read scopeCreate a Read & write key in Settings > API Keys
Browser does not open on auth loginNo BROWSER resolvedOpen the printed URL manually; the CLI keeps polling
Keychain access denied on macOSKeychain lockedUnlock the login keychain in Keychain Access

Next