Run codex login for the ChatGPT browser flow, which bills against your ChatGPT plan. Run printenv OPENAI_API_KEY | codex login --with-api-key to sign in with a key and pay API rates. codex login status shows which is active, codex logout clears both. On a headless box use codex login --device-auth. Checked against OpenAI docs in August 2026.
codex loginuses your ChatGPT plan. It is the default path.- An API key is a deliberate sign-in:
printenv OPENAI_API_KEY | codex login --with-api-key. codex login statustells you which credential is active, and exits 0 when one exists.- Credentials cache to
~/.codex/auth.jsonor your OS credential store. - The CLI and the IDE extension share one cached login. Logging out of one logs out both.
- Headless:
codex login --device-authfirst, SSH port forward second.
Signing in
codex login # opens a browser, sign in with ChatGPT
codex login status # confirm what you actually got
codex logout # clears BOTH ChatGPT and API-key credentials
printenv OPENAI_API_KEY | codex login --with-api-key
What each credential buys and costs, checked August 2026.
| Sign-in | Bills as | Command | Cloud features |
|---|---|---|---|
| ChatGPT account | Your ChatGPT plan allowance | codex login | Yes |
| API key | Standard API rates, per token | codex login --with-api-key | No |
| Codex access token | Workspace entitlement | codex login --with-access-token | Enterprise automation |
Where the credential is actually kept
Codex caches your login and reuses it. For a ChatGPT sign-in it refreshes the token automatically during use, so an active session usually keeps working without another browser round trip.
# file | keyring | auto
cli_auth_credentials_store = "keyring"
| Value | Stores the credential in |
|---|---|
file | auth.json under CODEX_HOME, which defaults to ~/.codex |
keyring | Your operating system credential store |
auto | The OS store when available, otherwise auth.json |
mkdir -p ~/.codex-work
CODEX_HOME=~/.codex-work codex login
CODEX_HOME=~/.codex-work codex
# the directory must already exist; Codex will not create it
Everything Codex keeps lives under that root: config.toml, the cached credential, session transcripts under sessions/, skills, rules and logs. Pointing CODEX_HOME at a second directory is the supported way to run a second account, and it is what the multi-account tooling below does under the hood.
Signing in without a browser
The default flow opens a browser and waits for a callback on localhost:1455. Two things break that: no browser, or a network configuration that blocks the loopback callback. There are four documented answers, in order.
Device code, the preferred route
codex login --device-auth
Beta as of August 2026. Turn on device-code login in your ChatGPT security settings for a personal account, or in workspace permissions if an admin owns the workspace. Then open the printed link on any device and enter the one-time code. You can also pick Sign in with Device Code in the interactive login screen.
Forward the callback over SSH
ssh -L 1455:localhost:1455 user@remote
# then, inside that SSH session
codex login
The normal browser flow now works: the tunnel carries the callback back to the machine that has the browser.
Copy a credential you already have
# to a remote host
ssh user@remote 'mkdir -p ~/.codex && cat > ~/.codex/auth.json' < ~/.codex/auth.json
# into a container
CONTAINER_HOME=$(docker exec MY_CONTAINER printenv HOME)
docker exec MY_CONTAINER mkdir -p "$CONTAINER_HOME/.codex"
docker cp ~/.codex/auth.json MY_CONTAINER:"$CONTAINER_HOME/.codex/auth.json"
This only works with file-based storage. If your machine is using the OS credential store, set cli_auth_credentials_store = "file" first or there is nothing to copy.
Use a key or an access token for anything unattended
# API key, the normal answer for CI
printenv OPENAI_API_KEY | codex login --with-api-key
# ChatGPT Enterprise access token, for trusted workspace automation
printenv CODEX_ACCESS_TOKEN | codex login --with-access-token
A ChatGPT plan is licensed for interactive use by a person. Automation should hold its own credential regardless of how convenient the alternative looks.
Automation, keys, and the leak nobody expects
CODEX_API_KEY="sk-..." codex exec --sandbox read-only "review the diff on this branch"
The three credential-carrying variables and what each is for.
| Variable | Read by | Use for |
|---|---|---|
CODEX_API_KEY | codex exec only | A single non-interactive run |
CODEX_ACCESS_TOKEN | CLI, app server, trusted automation | Enterprise workspace automation |
CODEX_CA_CERTIFICATE | HTTPS, login and WebSocket clients | Corporate TLS interception; falls back to SSL_CERT_FILE |
export CODEX_CA_CERTIFICATE=/path/to/corporate-root-ca.pem
codex login
When login fails
Ranked by how often it is actually the cause.
| Symptom | Cause | Fix |
|---|---|---|
| Browser never returns to the CLI | No browser, or the loopback callback is blocked | codex login --device-auth, or forward port 1455 |
| Works locally, fails over SSH | No browser on the remote host | Device code, tunnel, or an API key |
| Asked to sign in on every launch | ~/.codex is not writable by your user | ls -ld ~/.codex, then fix ownership |
| Signed out of the CLI after using the IDE extension | They share one cached login | Expected. Sign in again in either one |
| TLS or certificate errors during login | Corporate proxy or private root CA | Set CODEX_CA_CERTIFICATE to a PEM bundle |
| Logged out immediately at start-up | forced_login_method or a workspace restriction does not match | Sign in with the method your admin requires |
| Cloud features missing | You signed in with an API key | Sign in with ChatGPT; API keys have no Codex cloud |
codex login status # which credential is active
codex doctor # installation, config, auth, runtime, git, terminal
A direct codex login run also writes codex-login.log under your configured log directory. That is the file to attach when a browser or device-code flow fails in a way the terminal output does not explain.
Managed workspaces
# Only allow ChatGPT login, or only allow API key login.
forced_login_method = "chatgpt" # or "api"
# Restrict ChatGPT logins to one workspace.
forced_chatgpt_workspace_id = "00000000-0000-0000-0000-000000000000"
If the active credential does not match those restrictions, Codex logs you out and exits. That is the intended behavior, not a bug, and it is why a fresh laptop at a managed company can refuse a login that worked at home.
Questions people ask
Run codex login. It opens a browser for the ChatGPT sign-in flow and then caches the credential, so Codex uses your existing ChatGPT plan rather than metered billing.
Pipe it in: printenv OPENAI_API_KEY | codex login --with-api-key. That stores the key as the active credential and bills at standard API rates. It is a deliberate sign-in, not something that happens because the variable is set.
Run codex login status. It reports the active authentication method and exits 0 when credentials are present, which makes it usable as a guard in scripts. Inside a session, /status shows the same context alongside the model and sandbox.
In ~/.codex/auth.json by default, or in your operating system credential store. Choose with cli_auth_credentials_store in config.toml: file, keyring, or auto. CODEX_HOME moves the whole directory.
Use codex login --device-auth, which is in beta and needs device-code login enabled on your account or workspace. Otherwise forward the callback with ssh -L 1455:localhost:1455, copy an existing auth.json, or sign in with an API key.
codex logout then codex login replaces the credential. To keep both at once, point CODEX_HOME at a second directory and sign in there, so each account has its own config root, credential and session history.
They share one cached login. Logging out from either the CLI or the extension means the next launch of the other one needs a fresh sign-in.
No. An API key covers the CLI, the SDK and the IDE extension, but not Codex cloud or the cloud-attached features such as GitHub code review and the Slack integration.
Sources
Every figure above was read from these pages on August 2026. Vendors reprice without notice; if you find a stale number, tell us.