You install Codex CLI, run codex login, and a browser pops open asking you to sign in with ChatGPT. Fine — you click through, and you're coding. Then a week later you try to wire Codex into a CI pipeline, set OPENAI_API_KEY, and nothing works. The CLI keeps insisting your key isn't set, even though it plainly is. Welcome to the single most confusing part of Codex CLI: it has two authentication methods that behave differently, bill differently, and occasionally fight each other.
This guide untangles the two methods, gives you a clear rule for which to use, and walks through the troubleshooting for the bugs people actually hit.
The two auth methods
Codex CLI supports exactly two ways to authenticate:
- Sign in with ChatGPT (OAuth) — the default path when there's no valid session. Run
codex login, a browser opens for ChatGPT OAuth, and the CLI reads the returned access token and caches it locally. - API key (
OPENAI_API_KEY) — you generate a key in the OpenAI dashboard and supply it during login.
They are not interchangeable. They differ in three ways that matter: billing, permissions/data handling, and which features they unlock.
Billing
This is usually the deciding factor.
| ChatGPT sign-in | API key | |
|---|---|---|
| Billing model | Subscription-based (included with your plan) | Usage-based OpenAI Platform billing |
| Included plans | Plus, Pro, Business, Edu, Enterprise | N/A — pay per token at standard API rates |
| Usage limits | Plan's included Codex limits | Whatever your API spend allows |
If you have a ChatGPT plan that includes Codex, signing in with ChatGPT means your usage draws on plan-included limits rather than racking up per-token API charges. If you authenticate with a key, every request hits standard API pricing.
The rule of thumb: Sign in with ChatGPT for anything a human drives interactively. Use an API key for anything a machine runs unattended — scripts, CI/CD jobs, service accounts, and server-controlled agents.
Permissions and data handling
Your auth method also decides whose rules apply to your usage:
- ChatGPT sign-in follows your ChatGPT workspace permissions and RBAC, and respects ChatGPT Enterprise retention and data-residency settings.
- API key follows your API organization's retention and data-sharing settings instead.
For a regulated environment, this isn't a detail — the same prompt can land under different retention policies depending purely on how you logged in.
Feature availability
One hard constraint: Codex cloud requires ChatGPT sign-in. API keys are not a supported sign-in path for Codex cloud. API key auth works for the local CLI and the IDE extension only. Some features that lean on ChatGPT workspace access or cloud services are limited or unavailable under API key auth, so if you depend on those, ChatGPT sign-in isn't optional.
Where credentials live
By default, Codex caches credentials at ~/.codex/auth.json. These are plaintext access tokens — treat the file like a password. Don't commit it, don't bake it into a Docker image, don't paste it into a Slack thread.
You can change where credentials are stored with the cli_auth_credentials_store setting:
file— the defaultauth.jsonbehaviorkeyring— your OS credential storeauto— use the OS store, fall back toauth.json
On a shared or hardened machine, keyring (or auto) is the safer default.
Authenticating in CI and headless environments
Interactive OAuth assumes a browser. CI runners and remote boxes don't have one, so Codex gives you several paths.
For non-interactive execution, codex exec runs Codex with no TUI — you pass a task prompt as an argument, and it's built for CI pipelines and scheduled jobs. It reuses saved CLI auth by default.
For a key scoped to exactly one run, CODEX_API_KEY is supported only in codex exec. Set it inline so it never persists:
CODEX_API_KEY=<api-key> codex exec --json "triage open bug reports"
For headless interactive login, device code auth (beta) is available:
codex login --device-auth
And you can pipe an access token straight in:
printenv CODEX_ACCESS_TOKEN | codex login --with-access-token
The CI security pattern that actually matters
This is the part people skip and regret. Do not set OPENAI_API_KEY or CODEX_API_KEY as a job-level environment variable in any workflow that checks out or runs repository-controlled (untrusted) code. Untrusted code in the repo can read job-level env vars and exfiltrate your key.
For GitHub Actions, use the openai/codex-action, which starts a secure proxy for the key instead of exposing it to shell steps.
The recommended hardened pattern separates concerns into two jobs:
- A Codex job with
contents: readonly (no write access) that serializes its output as an artifact. - A separate PR-applying job with
contents: writethat consumes the artifact — and never receives the API key.
Useful hardening flags for the Codex job include --ignore-user-config, --ignore-rules, and --skip-git-repo-check.
Troubleshooting
"OPENAI_API_KEY is set but Codex ignores it" / the prompt loops
This is the big one, and it's a confirmed bug — not your mistake. Switching to API key does not work while a ChatGPT (Team/Plus/Pro) login is active. You run something like codex --config preferred_auth_method="apikey", select "Provide your own API key," and it loops back to the prompt with:
To use Codex with the OpenAI API, set OPENAI_API_KEY in your environment
…even though the variable is set. It's been reported across multiple CLI releases (issues #2733 on 0.23/0.24 and #3286 on 0.30.0), and the same defect is tracked separately as "Sign in with API key via environment variable cannot be used if ChatGPT subscription login is active." It's labeled a bug and assigned to an OpenAI engineer; as of the reports there was no documented in-thread fix or version that resolves it, so verify against your current CLI version.
Workaround: clear the cached ChatGPT credentials first, then set the key and re-authenticate.
codex logout # or: rm -f ~/.codex/auth.json
export OPENAI_API_KEY=<your-key>
codex login # re-authenticate with the key
The annoyance is that codex logout removes saved credentials for both API key and ChatGPT auth, so you'll have to log out and back in each time you switch methods.
Forcing API key mode via config
If you want to pin API key mode, some users set preferred_auth_method = "apikey" in ~/.codex/config.toml and make sure ~/.codex/auth.json contains only the OPENAI_API_KEY entry (remove any conflicting ChatGPT entries).
One caveat worth flagging: the official auth.md managed-config docs reference forced_login_method, while preferred_auth_method shows up in GitHub issues and CLI commands. They may not be the same key. Verify the exact naming against the version you're running before committing it to a shared config.
Checking what's active
To see which mode you're in, codex login status prints the active authentication mode and exits successfully when credentials are present. Run it first whenever auth behaves unexpectedly — half the "it's broken" reports are just a stale session from the other method.
"Signing in with ChatGPT created a billable API key"
You may find an older report (issue #2000) where a Pro user said "Sign in with ChatGPT" auto-generated a billable API key, and revoking it broke the CLI with a 401. Treat this as past behavior: OpenAI subsequently added the ability to sign in with ChatGPT and use subscription-included limits. If you see unexpected API charges after a ChatGPT login on a current version, that's worth reporting — but it's not the documented current behavior.
A few specifics worth verifying yourself
These tools move fast, so confirm the following against OpenAI's official pages before you rely on a number:
- The exact ChatGPT Pro price is reported inconsistently across third-party sources — check OpenAI's pricing page.
- A promotion offered Plus users $5 and Pro users $50 in free API credits for signing in to Codex CLI with ChatGPT, originally a 30-day window. It may well have expired; don't count on it.
- Whether the API-key switching bug has been fixed in the latest release — check the current version and the linked issues.
Bottom line
Pick by who's driving. A human at a keyboard → Sign in with ChatGPT — it's the default, it uses your subscription's included limits instead of per-token billing, and it's the only path to Codex cloud. A machine running unattended → API key, ideally scoped to a single codex exec run with CODEX_API_KEY and never exposed as a job-level env var in untrusted workflows.
The one trap to remember: the two methods don't switch cleanly. If your API key is being "ignored" and the prompt loops, you almost certainly have a live ChatGPT session in the way. Run codex logout, set your key, log back in — and check codex login status whenever auth surprises you.