Claude Code with Multiple Accounts on One Machine
TL;DR >> The clean way to use Claude Code with both your normal account and z.ai is one neutral config and two simple entry points. <<
If you want two Claude Code entry points, one for your normal Claude Team or Enterprise login and one for an alternative API provider like z.ai, the cleanest answer is not two installs.
Tested with Claude Code 2.1.72.
What you actually want is one Claude install, one neutral global config, and two explicit commands:
claude-teamfor your normal first-party Claude loginclaude-zaifor the z.ai gateway using a token sourced outside Claude settings
The names are arbitrary. You could call them claude-default and claude-zai if you prefer. The important part is the pattern: use one Claude install and one global Claude config, and select the provider with wrapper scripts instead of swapping config files or maintaining a second install.
If you want to try z.ai itself, here is the same referral link I used before: Get GLM Coding Plan.
Most of the confusion around this topic comes from the fact that Claude Code has two different layers of state. Your saved first-party login lives separately from settings.json, but global env overrides still affect every session.
That sounds harmless until you realise it means you can be correctly logged into your normal Claude account and still accidentally route every request through z.ai if you set gateway variables globally.
# The mistake to avoid
If you put this in ~/.claude/settings.json:
{
"env": {
"ANTHROPIC_AUTH_TOKEN": "...",
"ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic"
}
}
then every Claude session goes through that gateway.
You have effectively made z.ai the default for every Claude Code session on that machine.
That is the trap most people hit.
The clean fix is:
- keep
~/.claude/settings.jsonprovider-neutral - source the z.ai token outside Claude settings
- use
claude-teamwhen you want the normal Claude path - use
claude-zaiwhen you want the z.ai path
# What to build instead
This is the target end state:
~/.claude/settings.jsonis provider-neutral- z.ai token is sourced outside Claude settings
claude-teamandclaude-zailive in~/bin- no repo-local Claude config is required
- no
~/claude-zhipuinstall is required - no legacy
claude-zhipuwrapper is required
If you keep shell tools in dotfiles, the wrappers can live there and be symlinked into ~/bin.
Example:
~/dev/dotfiles/claude/.claude/settings.json~/dev/dotfiles/bin/bin/claude-team~/dev/dotfiles/bin/bin/claude-zai
Before changing anything, make sure Claude Code is installed and reachable as claude, and that ~/bin is on your PATH.
claude --version
echo $PATH | tr ':' '\n' | grep -x "$HOME/bin"
# Where the z.ai token should live
The key rule is simple: do not put the token in ~/.claude/settings.json.
You have a few reasonable options:
pass, if you already use password-store- a local secret file such as
~/.config/claude/zai-token - an environment variable such as
CLAUDE_ZAI_TOKEN
pass is the most security-conscious option in this guide, but it is not required.
If you want to use pass, this guide uses:
pass show api/zhipu
If you do not have one yet:
pass insert api/zhipu
The broader point is simple: Claude settings should stay clean, and the z.ai credential should only be injected when you intentionally choose the z.ai path.
# Keep global Claude settings boring
Your global Claude settings should keep only normal defaults such as status line, plugins, model preference, and harmless flags.
Example:
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"env": {
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
},
"model": "opus",
"statusLine": {
"type": "command",
"command": "input=$(cat); current_dir=$(echo \"$input\" | jq -r '.workspace.current_dir // .cwd'); model=$(echo \"$input\" | jq -r '.model.display_name'); dir_name=$(basename \"$current_dir\"); printf \"%s %s\" \"$dir_name\" \"$model\""
}
}
Once global settings are neutral, the rest of the setup becomes straightforward. You create one wrapper that clears provider-specific overrides and one wrapper that opts into the z.ai gateway.
# The normal path: claude-team
This wrapper clears provider-specific env vars and launches the normal Claude binary.
#!/usr/bin/env bash
set -euo pipefail
unset ANTHROPIC_API_KEY
unset ANTHROPIC_AUTH_TOKEN
unset ANTHROPIC_BASE_URL
unset ANTHROPIC_DEFAULT_HAIKU_MODEL
unset ANTHROPIC_DEFAULT_SONNET_MODEL
unset ANTHROPIC_DEFAULT_OPUS_MODEL
unset ANTHROPIC_MODEL
unset API_TIMEOUT_MS
unset CLAUDE_CONFIG_DIR
exec claude "$@"
Save it as:
~/bin/claude-team
chmod +x ~/bin/claude-team
The entire purpose of this wrapper is to make sure an old API key, gateway URL, or model mapping does not bleed into the first-party Claude path.
# The z.ai path: claude-zai
This wrapper resolves the token from an env var, a local token file, or pass, then points Claude at the z.ai gateway and sets the model mapping env vars.
#!/usr/bin/env bash
set -euo pipefail
PASS_ENTRY="${CLAUDE_ZAI_PASS_ENTRY:-api/zhipu}"
TOKEN_FILE="${CLAUDE_ZAI_TOKEN_FILE:-$HOME/.config/claude/zai-token}"
if [[ -n "${CLAUDE_ZAI_TOKEN:-}" ]]; then
ZAI_TOKEN="$CLAUDE_ZAI_TOKEN"
elif [[ -f "$TOKEN_FILE" ]]; then
ZAI_TOKEN="$(head -n1 "$TOKEN_FILE")"
elif command -v pass >/dev/null 2>&1; then
ZAI_TOKEN="$(pass show "$PASS_ENTRY" 2>/dev/null | head -n1 || true)"
else
ZAI_TOKEN=""
fi
if [[ -z "$ZAI_TOKEN" ]]; then
echo "Set CLAUDE_ZAI_TOKEN, create $TOKEN_FILE, or store the token in pass at $PASS_ENTRY"
exit 1
fi
unset ANTHROPIC_API_KEY
unset ANTHROPIC_MODEL
unset CLAUDE_CONFIG_DIR
export ANTHROPIC_AUTH_TOKEN="$ZAI_TOKEN"
export ANTHROPIC_BASE_URL="https://api.z.ai/api/anthropic"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="glm-4.5-air"
export ANTHROPIC_DEFAULT_SONNET_MODEL="glm-4.7"
export ANTHROPIC_DEFAULT_OPUS_MODEL="glm-5"
export API_TIMEOUT_MS="3000000"
exec claude "$@"
Save it as:
~/bin/claude-zai
chmod +x ~/bin/claude-zai
If you want the simplest possible version, you can skip pass entirely and create a local token file:
mkdir -p ~/.config/claude
printf '%s\n' 'YOUR_ZAI_TOKEN' > ~/.config/claude/zai-token
chmod 600 ~/.config/claude/zai-token
This wrapper is the only place where provider-specific configuration should live.
# Dotfiles are optional, but convenient
If you maintain shell tools in dotfiles, keep the real files there and symlink them into ~/bin. If you do not use dotfiles, you can skip this section and keep the wrappers directly in ~/bin.
Example:
ln -sfn ../dev/dotfiles/bin/bin/claude-team ~/bin/claude-team
ln -sfn ../dev/dotfiles/bin/bin/claude-zai ~/bin/claude-zai
This makes the setup portable across machines and keeps the implementation in one place.
# What daily usage feels like
Normal Claude path:
claude-team
z.ai path:
claude-zai
So the mental model becomes:
claude-teammeans “use the saved first-party Claude login”claude-zaimeans “use the z.ai gateway with a token sourced outside Claude settings”
That is what makes this setup pleasant. You are not editing files or trying to remember which provider is currently configured. You are just choosing the right entry point.
# How to verify it actually works
Check the normal path:
claude-team auth status --text
Check the z.ai path:
claude-zai auth status --text
Expected behavior:
claude-teamshould not show the z.ai base URLclaude-zaishould showhttps://api.z.ai/api/anthropic
# The one confusing part: auth banners
This is the subtle part, and it is easy to misread when testing.
claude-team only clears env overrides. It does not magically switch your saved Claude account to the correct Team or Enterprise org.
If your saved first-party login is still an API-side account, the banner may still show Claude API even though the z.ai gateway is gone. That usually means the wrapper is correct, but the stored Claude login still needs to be switched.
Check it with:
claude-team auth status --json
If needed, re-login with the correct company account:
claude auth login --sso --email [email protected]
Or launch claude-team and run:
/login
This matters because the wrapper fixes provider overrides, but your stored first-party account state still determines whether Claude sees you as API, Pro, Team, or Enterprise.
In other words, if claude-team still says Claude API, that does not automatically mean the wrapper failed. It can also mean you are logged into the wrong first-party account context.
# Security
- Do not keep the z.ai token in
~/.claude/settings.json - If the token ever lived in a tracked file, rotate it
- Prefer
passor a local secret file over hardcoding secrets into wrappers or config
If you want to try z.ai directly, here is the referral link again:
The short version is this: one Claude install, one boring global config, two explicit entry points.
This is the version I would recommend to anyone who wants one Claude Code setup for their normal Claude account and a second explicit path for z.ai.
# Additional Resources
- Official Zhipu Claude Development Guide
- GLM-4.7 Model Announcement
- Get GLM Coding Plan — Affiliate link, gives you additional 10% off