Documentation menu
Billing: subscription or API key
Claude Code, Codex and Gemini CLI can run on two kinds of credentials:
- subscription: the plan you are logged in with in the CLI (Claude Pro/Max/Team via claude.ai, a ChatGPT plan for Codex, a Google account login for Gemini). It is a flat fee with usage limits.
- api: a metered API key (
ANTHROPIC,_API _KEY OPENAI_API_KEY,GEMINI_API_KEY). You pay per token.
nid picks one per session, so a stray ANTHROPIC in your shell doesn't silently bill
your API account while you meant to use your Max plan. It also works the other way: you can run
selected sessions on an API key while the rest use the subscription.
Modes#
| Mode | What nid does |
|---|---|
auto (default) | Use the subscription login when the CLI has one; otherwise use an API key; otherwise fail the start and say how to log in. |
subscription | Start the agent with its API-key variables removed, so the CLI uses its login. Fails the start when there is no subscription login. |
api | Start the agent with an API key from the daemon's environment or from nid auth set. Fails the start when there is no key. |
How each mode is applied:
| Agent | subscription | api |
|---|---|---|
| claude | removes ANTHROPIC and ANTHROPIC; login checked with claude auth status (a claude.ai account, not an API key or Bedrock/Vertex) | sets ANTHROPIC |
| codex | removes OPENAI_API_KEY and CODEX_API_KEY, selects the built-in openai provider with forced, checks account/read and reports the ChatGPT plan | adds a model provider nid-openai-api (env, requires) and selects it, so Codex reads the key without replacing your stored ChatGPT login (~/) |
| gemini (preset) | removes GEMINI_API_KEY and GOOGLE_API_KEY; login = ~/ | sets GEMINI_API_KEY (untested; Gemini also follows its own selectedAuthType setting) |
| opencode, kimi | not managed: they bill through their own provider configuration (opencode auth login; kimi login / kimi provider) | not managed |
| other custom agents | not managed: launched unchanged | not managed |
| mock (test driver) | simulated: recorded, nothing changes | simulated |
Unmanaged agents accept only auto. Asking for subscription or api explicitly fails the
start with the reason.
nid decides the billing every time an agent starts (spawn, resume, crash restart, rotation).
A new login or a new key therefore applies on the next resume. The result is stored on the
session (Session) and logged as a notice, for example
billing: subscription (claude.
Native takeover (attach --native) keeps the session's resolved mode. The attaching CLI
resolves credentials locally; keys are never transferred in the takeover RPC response.
For API mode, the attaching shell must have a key or access to the same nid secret
store. If it cannot resolve the mode, it fails and hands the session back to the daemon.
Native turns remain outside nid's usage accounting.
Where to set it#
Most specific wins: session > group > ~/ > auto.
# ~/.nid/config.yaml: default for everything
billing: auto
# ~/.nid/config.yaml: or per agent, with an optional default
billing:
default: subscription
codex: api
# nid.yaml
groups:
web:
billing: { claude: subscription, codex: auto } # group setting (same shapes as above)
sessions:
- claude:lead
- agent: codex
name: bulk
billing: api # this session only
nid spawn claude:lead --billing subscription
nid group set web billing='{"codex":"api"}' # change a running group's setting
The dashboard's Spawn form has a Billing field. Session pages show the billing, and the group
table marks API-billed sessions with an api badge.
API keys#
nid uses an API key from the daemon's environment first, for example ANTHROPIC
exported in the shell that started the daemon. Otherwise it uses a key stored with:
nid auth set claude --api-key # prompts without echo; or pipe it: echo $KEY | nid auth set claude
nid auth set codex --api-key sk-... # inline (ends up in your shell history)
nid auth rm claude
Where stored keys live:
- macOS: the login keychain (service
nid, account = agent name). - Linux: the Secret Service via
secret-toolwhen it works (GNOME Keyring, KWallet). - Windows:
~/, encrypted with DPAPI for the current Windows user..nid/ secrets/ <agent>.dpapi - Unix fallback when the keychain tool fails:
~/, mode 0600. The confirmation names the plaintext file explicitly. Windows DPAPI failures abort; nid does not fall back to plaintext there, and an existing key is kept..nid/ secrets/ <agent>.key
The default home's keyring service is nid. A custom NID_HOME gets a service
name derived from that directory, so disposable homes and separate installations cannot
read or delete each other's keys. auth rm removes that home's file and keyring entry.
Confirming data removal during uninstall --purge also clears its managed keyring entries.
A locked/unavailable keyring with recorded entries stops the purge so it can be retried.
Keys go to keychain helpers on stdin, not their command lines (an inline --api-key still
appears in the nid command line and shell history). The daemon reads the key when a
session starts and passes it through the agent environment. Vendor child processes can
inherit that environment. nid does not put the key in session billing metadata, notices
or generated MCP configuration; arbitrary agent output is not a secret-redaction boundary.
Falling back to the API key on rate limits (opt-in)#
When a subscription plan hits its usage limit, nid normally holds the session until the
window resets (see provider rate limits). With fallback: api it keeps
working on your API key instead:
# nid.yaml (per group) or ~/.nid/config.yaml (default for every group)
fallback: api # api | off (default off)
nid group set web fallback=api turns it on for a running group. When a session that runs on
its subscription becomes rate-limited, nid:
- checks that an API key is available for the agent (environment or
nid auth set) and that the group's budget is not spent; otherwise it says why in the session log and waits as usual; - restarts the agent on the same conversation with API billing (after the current turn);
- records the switch as a session notice, a board note, a human inbox message and a
limitnotification; - when the subscription window resets (the reset time the provider reported), switches back to the subscription the same way, between turns, and records that too.
Turns made during the fallback are billed spend and count against budgetUsd; once the budget
is spent automatic turns pause as usual. Session ({until, since, reason}) shows
an active fallback; a daemon restart keeps it until until. Only sessions currently billed on a
subscription switch: sessions already on api and unmanaged agents (opencode, kimi, custom) are
never switched. The fallback is per session; other sessions of the group keep their own billing.
Checking#
nid auth status # per agent: what a new session would get, the subscription login, where an API key would come from
nid doctor # includes the same lines for installed agents
nid ls # BILLING column: sub, api (highlighted) or - (unmanaged / not started)
nid status # lists API-billed sessions per group (api:<session>)
Notes#
autowith both a login and a key present uses the subscription, and the key in the environment is removed for that agent. Useapito force the key.nid auth statusrunsclaude auth statusandcodex login status. Checks are fresh for each launch; a different custom command or login directory cannot reuse an old result.- nid never logs you in or out of a CLI and never edits the CLI's own credential files. Switching billing only changes how nid launches the agent.
- Billing modes steer the supported CLI launch paths, not arbitrary vendor configuration.
Explicit
extraArgscan override nid's flags. Vendor settings, API key helpers, cloud providers and Gemini'sselectedAuthTypecan also affect authentication; avoid conflicting settings and check the vendor's reported mode. Gemini remains unverified.
Codex launch settings were checked against the 0.160.1 configuration schema.