Authentication
l4 resolves credentials in a fixed order on every invocation:
- The
--token/-tflag - The
LEVELFOUR_TOKENenvironment variable - 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 loginThis 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 --forceTo clear the stored token:
l4 auth logoutConfirm 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-1234read 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.
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 --jsonIn 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 listThe 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 APIwhoami 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 whoamiThe 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
| 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 owns the exit codes and the full command catalogue
- Installation if
l4is not on this machine yet - CI/CD integration has the GitHub Actions and GitLab CI workflows in full
- Connecting a client takes these same keys, or signs you in instead
Installation
Get l4 onto a laptop or a CI image with Homebrew, go install or a prebuilt archive, and confirm the install worked.
Output formats
Every command takes the same output flags, from the default table to JSON, an in-process jq filter, a Go template, CSV from the export commands, or nothing but an exit code.