Route your OpenClaw API requests through your Claude Max/Pro subscription instead of Extra Usage billing.
After Anthropic revoked subscription billing for third-party tools (April 4, 2026), OpenClaw requests are billed to Extra Usage. This proxy sits between OpenClaw and the Anthropic API, injecting Claude Code's billing identifier so requests use your existing subscription.
Zero cost increase. Full OpenClaw functionality. No code changes to OpenClaw.
The proxy performs 7-layer bidirectional request/response processing to defeat Anthropic's multi-layer detection:
Outbound (request to API):
- Billing Header -- Injects an 84-character Claude Code billing identifier into the system prompt
- Token Swap -- Replaces OpenClaw's auth token with your Claude Code OAuth token
- String Sanitization -- Replaces 30 trigger phrases (OpenClaw, sessions_*, HEARTBEAT, etc.)
- Tool Name Bypass -- Renames all 29 OpenClaw tool names to PascalCase Claude Code convention (e.g.,
exec->Bash,lcm_grep->ContextGrep) throughout the entire body - System Template Bypass -- Strips ~28K of structured config sections and replaces with a ~0.5K natural prose paraphrase
- Tool Description Stripping -- Removes tool descriptions to reduce fingerprint signal
- Property Renaming -- Renames OC-specific schema properties (e.g.,
session_id->thread_id)
Inbound (response to OpenClaw): 8. Full Reverse Mapping -- Restores ALL original tool names, property names, file paths, and identifiers in both SSE streaming chunks and JSON responses
This ensures Anthropic sees what looks like a Claude Code session while OpenClaw sees its original tool names, paths, and identifiers.
- Node.js 18+
- Claude Max or Pro subscription
- Claude Code CLI installed and authenticated
- OpenClaw installed and running
npm install -g @anthropic-ai/claude-codeThen authenticate with your Claude account:
claude auth loginThis opens a browser window to sign in with your Claude Max/Pro account. Once authenticated, credentials are stored at ~/.claude/.credentials.json. The proxy reads from this file.
Verify it worked:
claude auth status
# Should show: loggedIn: true, subscriptionType: max (or pro)# 1. Clone
git clone https://github.com/zacdcook/openclaw-billing-proxy
cd openclaw-billing-proxy
# 2. Run setup (auto-detects your config)
node setup.js
# 3. Start the proxy
node proxy.js
# 4. Update OpenClaw config (see "OpenClaw Configuration" below)
# 5. Restart your OpenClaw gatewayAdd or update the models.providers.anthropic section in ~/.openclaw/openclaw.json to point at the proxy:
{
"models": {
"providers": {
"anthropic": {
"baseUrl": "http://127.0.0.1:18801"
}
}
}
}Important notes:
- The
baseUrlfield is the ONLY mechanism that routes OpenClaw traffic through the proxy. Environment variables likeANTHROPIC_BASE_URLdo NOT control OpenClaw's routing. - If you have a direct Anthropic API key in your auth profiles (
~/.openclaw/agents/*/agent/auth-profiles.json), OpenClaw may prefer that over OAuth and bypass the proxy entirely. Remove or disable the API key auth profile to ensure all traffic goes through the proxy. - After updating, restart your OpenClaw gateway for the changes to take effect.
- Run
node troubleshoot.jsto verify the proxy is working AND that OpenClaw is pointed at it.
To stop using the proxy, change baseUrl back to https://api.anthropic.com (or remove the models.providers section entirely) and restart the gateway.
The config.json file (generated by setup or created manually):
{
"port": 18801,
"credentialsPath": "~/.claude/.credentials.json",
"replacements": [
["OpenClaw", "OCPlatform"],
["openclaw", "ocplatform"],
["sessions_spawn", "create_task"],
["sessions_list", "list_tasks"],
["sessions_history", "get_history"],
["sessions_send", "send_to_task"],
["sessions_yield", "yield_task"],
["running inside", "running on"]
],
"reverseMap": [
["OCPlatform", "OpenClaw"],
["ocplatform", "openclaw"],
["create_task", "sessions_spawn"],
["list_tasks", "sessions_list"],
["get_history", "sessions_history"],
["send_to_task", "sessions_send"],
["yield_task", "sessions_yield"]
]
}replacements -- Applied to outbound requests. Each [find, replace] pair does a raw string replacement on the request body before forwarding to the API.
reverseMap -- Applied to inbound responses. Each [sanitized, original] pair restores terms back to their original form before returning to OpenClaw. This ensures tool names, file paths, and identifiers work correctly.
Important: Use space-free replacements for lowercase openclaw. Replacing .openclaw/ with .assistant platform/ (with a space) breaks filesystem paths in tool calls. Use ocplatform or similar instead.
If your OpenClaw version has additional sessions_* tools (they add new ones across versions), add them to both replacements and reverseMap:
{
"replacements": [
["sessions_new_tool", "new_task_tool"]
],
"reverseMap": [
["new_task_tool", "sessions_new_tool"]
]
}If you have a custom assistant name that Anthropic blocks (test by checking if requests fail with the name present but pass without it), add it the same way.
# Start the proxy (uses ~/.claude credentials by default)
docker compose up -d
# Verify
curl http://127.0.0.1:18801/healthWith an OAuth token instead of credential file:
# Copy and edit the environment file
cp .env.example .env
# Set OAUTH_TOKEN in .env
docker compose up -dCustom port:
PROXY_PORT=9000 docker compose up -dCustom replacement rules:
Uncomment the config.json volume mount in docker-compose.yml, then create a config.json (copy from config.example.json and edit).
See .env.example for all available environment variables.
Note: macOS Keychain credential extraction does not work inside Docker. Use the
~/.claudevolume mount (default) or setOAUTH_TOKENin.env.
sudo tee /etc/systemd/system/openclaw-proxy.service << EOF
[Unit]
Description=OpenClaw Billing Proxy
After=network.target
[Service]
ExecStart=/usr/bin/node /path/to/proxy.js
Restart=always
User=YOUR_USER
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable openclaw-proxy
sudo systemctl start openclaw-proxyAdd to your gateway.cmd before the gateway launch:
start "Billing Proxy" /min node "C:\path\to\proxy.js"
timeout /t 2 /nobreak >nulpm2 start proxy.js --name openclaw-proxy
pm2 saveClaude Code's OAuth token expires every ~24 hours. The proxy reads the token fresh from disk on each request. To refresh:
- Easiest: Open Claude Code CLI briefly -- it auto-refreshes on startup
- Automated: Set up a cron that runs
claude -p "ping" --max-turns 1 --no-session-persistencedaily (triggers auth refresh)
curl http://127.0.0.1:18801/healthReturns token status, request count, uptime, subscription type, and pattern counts.
Anthropic uses four layers to detect third-party tools. v1.x only handled layers 1-2. v2.0 handles all four.
The API checks the system prompt for x-anthropic-billing-header. Without it, OAuth requests go to Extra Usage. Simple 84-char string match.
The classifier scans the full request body for known phrases: OpenClaw, sessions_spawn/list/history/send/yield/store, HEARTBEAT_OK, running inside, clawhub, clawd, etc. v1.x handled this. v2.0 expands to 30 patterns.
The API identifies OpenClaw by the combination of tool names in the request. This was proved definitively:
- Identical empty schemas (no descriptions, no properties) with original tool names = FAIL (2.8K body!)
- Same empty schemas with PascalCase CC-like names = PASS
The detector has a signature of OpenClaw's tool name set. When enough matching tool names appear together (threshold: ~25 tools), it flags the request. Individual tools pass; the combination triggers it.
The fix: rename all tool names to PascalCase Claude Code convention (exec -> Bash, message -> SendMessage, lcm_grep -> ContextGrep, etc.) throughout the entire body -- tools array, messages, and system prompt.
The structured config sections (## Tooling, ## Workspace, ## Messaging, ## Reply Tags, etc.) match a known template fingerprint. The threshold is ~26K characters. String replacements don't defeat this because the structure is preserved even when words change.
The fix: strip the entire config section (~28K) and replace with a ~0.5K natural prose paraphrase. The model still functions correctly because tool capabilities are conveyed through the tools array, and behavioral rules come from workspace docs (AGENTS.md, SOUL.md, etc.) which are kept intact.
The classifier scores the entire request body (system + tools + messages), not just the system prompt. Each signal source contributes to an overall score. This means all four layers must be addressed simultaneously for large conversation bodies.
Without reverse mapping, the model sees sanitized paths in its context (e.g., .ocplatform/workspace/scripts/) and uses them for tool calls. But the actual filesystem has .openclaw/. The reverse mapping translates API responses back to original terms before OpenClaw processes them, ensuring:
- Tool names match OpenClaw's tool registry
- File paths match the actual filesystem
- Session management commands use correct identifiers
Run the diagnostic script to identify issues:
node troubleshoot.jsThis tests 8 layers independently (credentials, token, API, billing header, trigger detection, proxy health, end-to-end) and tells you exactly what to fix.
"Could not find credentials file"
- Run
claude auth loginto authenticate (opens browser) - On Mac, the file may be at
~/.claude/credentials.json(no dot prefix) -- the proxy checks both - If the file exists but is empty (0 bytes), run
claude auth logout && claude auth login - If still empty on Mac, run
claude -p "test" --max-turns 1to force a credential write to disk
Proxy returns 400 "out of extra usage" (v2)
- If you upgraded from v1.x: the old string-only sanitization no longer works. You need v2.0's full 7-layer processing. Make sure you're running the new
proxy.js. - Check
/healthendpoint -- it should showversion: "2.0.0"andlayersobject. - If v2 is running and still failing: your OpenClaw version may have new tools not in the default rename list. Check the proxy console for
DETECTION!log lines. Add custom tool renames toconfig.json. - If it was working and stopped: Anthropic may have added new detection. Check the repo for updates.
"Third-party apps now draw from your extra usage"
- Same cause as above
- Disable Extra Usage in your Claude settings to verify subscription billing
- With v2, restart the OpenClaw gateway to get a fresh conversation (accumulated history can contribute to detection score)
429 Rate Limit
- Normal if you have active Claude Code sessions sharing the rate bucket
- Wait a few minutes and try again
- Not a proxy issue
Token Expired
- Open Claude Code CLI briefly -- it auto-refreshes on startup
- Or run:
claude -p "ping" --max-turns 1 --no-session-persistence
HTTP 500: "Unexpected token" / Invalid JSON in credentials
- The credentials file has a UTF-8 BOM (byte order mark) -- an invisible character at the start
- This happens when PowerShell or other editors rewrite the file, or after token auto-refresh
- v1.4.1+ handles this automatically by stripping the BOM before parsing
- Manual fix:
node -e "const fs=require('fs');let c=fs.readFileSync(process.env.HOME+'/.claude/.credentials.json','utf8');if(c.charCodeAt(0)===0xFEFF)fs.writeFileSync(process.env.HOME+'/.claude/.credentials.json',c.slice(1))" - Verify with:
node -e "JSON.parse(require('fs').readFileSync(process.env.HOME+'/.claude/.credentials.json','utf8'));console.log('OK')"
Tool execution fails / wrong file paths
- If the model references
.ocplatform/instead of.openclaw/, yourreverseMapis missing entries - Ensure every
replacementsentry has a matchingreverseMapentry - Use space-free replacements (e.g.,
ocplatform, NOTassistant platform) to avoid breaking filesystem paths
Empty credentials file on Mac / Keychain credentials
- Newer Claude Code versions store tokens in macOS Keychain instead of a file
- The proxy checks these Keychain service names:
Claude Code-credentials,claude-code,claude,com.anthropic.claude-code - Check manually:
security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null - Run
node setup.jsto auto-extract the Keychain token to~/.claude/.credentials.json - Run
claude -p "test" --max-turns 1to force credential write if Keychain is also empty
This is an unofficial workaround. Anthropic may change their detection at any time. Use at your own risk. This proxy does not modify OpenClaw or Claude Code -- it's a transparent HTTP middleman.
MIT