Subscription Auth
Use your Claude Pro / Qwen CLI subscription instead of an API key
Use the subscription you already pay for — Claude Pro, Qwen CLI, etc. — to power your Agent without a separate API key.
from praisonaiagents import Agent
agent = Agent(
name="assistant",
instructions="You are helpful.",
llm="claude-haiku-4-5",
auth="claude-code",
)
agent.start("Hello")The user chats via the agent; subscription auth supplies credentials instead of a raw API key.
Quick Start
- Login to CLI
claude /login(Claude Pro) — orqwen login - Use in Agent
python from praisonaiagents import Agent agent = Agent( name="assistant", instructions="You are helpful.", llm="claude-haiku-4-5", auth="claude-code", ) agent.start("Hello")
How It Works
Subscription Auth enables agents to use your existing subscriptions without requiring separate API keys.
Cloning & multi-channel
clone_for_channel() forwards auth=, so gateway and multi-channel setups that duplicate the agent per channel keep subscription billing.
from praisonaiagents import Agent
agent = Agent(name="t", llm="anthropic/claude-sonnet-4-5", auth="claude-code")
clone = agent.clone_for_channel()
# clone.auth == "claude-code"Supported Providers
Which subscription auth provider should you use?
auth= value | Subscription | Token source | Status |
|---|---|---|---|
claude-code | Claude Pro | ~/.claude/.credentials.json or macOS Keychain | ✅ Working |
qwen-cli | Qwen | ~/.qwen/oauth_creds.json | ✅ Working |
codex | ChatGPT Plus | ~/.codex/auth.json | ⚠️ Experimental |
gemini-cli | Gemini | ~/.gemini/oauth_creds.json | ⚠️ Experimental |
Common Patterns
No model needed
Skip llm= and the provider’s vendor model is chosen for you.
from praisonaiagents import Agent
# No llm= — defaults to anthropic/claude-sonnet-4-5 for claude-code,
# openai/qwen3-coder-plus for qwen-cli.
agent = Agent(name="t", auth="claude-code")
agent.start("Hello")Choose your refresh path
When a claude-code token expires, pick the action that matches your setup — PraisonAI never rotates it for you.
Telegram bot using your Claude subscription
from praisonaiagents import Agent
from praisonai.bots import Bot
agent = Agent(
name="claude-bot",
instructions="You are a helpful Telegram bot.",
llm="claude-haiku-4-5",
auth="claude-code",
)
bot = Bot("telegram", agent=agent)
bot.run()Switching between API key and subscription
from praisonaiagents import Agent
# Use your Claude Pro subscription
agent_subscription = Agent(
name="claude-subscription",
llm="claude-haiku-4-5",
auth="claude-code", # Uses your subscription
)
# Use API key (traditional method — set ANTHROPIC_API_KEY)
agent_api_key = Agent(
name="claude-api",
llm="claude-haiku-4-5",
)
# Both work identically
response1 = agent_subscription.start("Hello")
response2 = agent_api_key.start("Hello")Custom provider (advanced)
from praisonaiagents.auth import register_subscription_provider, SubscriptionCredentials
class MyAuth:
def resolve_credentials(self) -> SubscriptionCredentials:
return SubscriptionCredentials(
api_key="my-token",
base_url="https://my-provider.com/v1",
headers={"x-custom": "value"},
)
def refresh(self) -> SubscriptionCredentials:
return self.resolve_credentials()
def headers_for(self, base_url: str, model: str) -> dict:
return {"user-agent": "my-provider/1.0.0"}
register_subscription_provider("mine", lambda: MyAuth())Configuration Options
SubscriptionCredentials API ReferenceConfiguration options and types for subscription credentials
Best Practices
Fail-fast at construction
Agent(auth=...) resolves credentials eagerly at construction. An unknown provider id, missing credentials, or an experimental provider raises AuthError from the Agent(...) call itself rather than silently falling back to your API key at request time. This is deliberate — a silent downgrade means money leaves the wrong account.
Token Security
Tokens are read locally from your CLI tools and never persisted by PraisonAI. They stay in memory only during agent execution. PraisonAI does not refresh or write them back — see “Read-only for claude-code” below.
Backward Compatibility
Existing agents using API keys continue to work unchanged. Setting auth= is completely optional and doesn’t affect agents that don’t use it.
Read-only for claude-code
PraisonAI reads Claude Code OAuth credentials from macOS Keychain, ~/.claude/.credentials.json, or the ANTHROPIC_TOKEN / CLAUDE_CODE_OAUTH_TOKEN env vars, and uses them as-is. It never refreshes or persists tokens for auth="claude-code".
This is deliberate. Anthropic rotates OAuth refresh tokens on every use. If PraisonAI refreshed in-memory without writing back to the Keychain, it would silently invalidate the shared session used by the Claude CLI and any other tool on the same machine.
When your Keychain token expires, refresh it once via one of these paths — Claude CLI will keep it fresh from then on: – Run claude /login (interactive), OR – Let the Claude CLI run in the background — its own refresh keeps the Keychain entry valid, OR – Set ANTHROPIC_TOKEN (or CLAUDE_CODE_OAUTH_TOKEN) to a long-lived token — env vars take precedence over Keychain/file.
Programmatic provider.refresh() for claude-code raises AuthError with a message telling you exactly what to do next. This is not a bug — it’s the guard that keeps shared sessions safe.
Don't commit token files
Never commit ~/.claude/.credentials.json, ~/.qwen/oauth_creds.json, or similar files to version control. These contain sensitive authentication data.
Troubleshooting
“No Claude Code credentials found”
Solution: Install Claude Code CLI and run claude /login to authenticate with your Claude Pro account.
# Install Claude Code (if not already installed)
# Then login with your Claude Pro account
claude /logininvalid_request_error on a Claude Code request
What it means: This is a genuine client-side error (a malformed request), not a retryable auth failure. PraisonAI no longer misclassifies it as an expired-token error, so it surfaces directly instead of being silently retried.
Solution: Check the request itself — model name, message shape, or parameters. If you also see auth failures, re-authenticate the shared session:
claude /loginmacOS Keychain prompt every run
Solution: Grant security command access to avoid repeated prompts. This is handled automatically by the system after first access.
Anthropic 500 errors
Issue: OAuth tokens require specific headers that are automatically included.
What PraisonAI does: Automatically sends required headers:
user-agent: claude-cli/<version> (external, cli)x-app: clianthropic-beta: interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14,claude-code-20250219
Codex / Gemini raise AuthError
Expected behavior: These providers are experimental and not yet usable. They raise AuthError at construction time — Agent(auth="codex") raises immediately, not at first request.
from praisonaiagents import Agent
Agent(name="t", auth="codex")
# AuthError: Codex provider is experimental — needs Responses API transport wiringAuthError: Unknown subscription provider
Cause: A typo or unregistered id in auth=. Valid ids are claude-code, codex, gemini-cli, and qwen-cli. The typo now surfaces at construction rather than silently billing your API key.
from praisonaiagents import Agent
from praisonaiagents.auth.subscription.protocols import AuthError
try:
Agent(name="t", llm="gpt-4o-mini", auth="nonexistent-provider")
except AuthError as e:
print(e)
# Unknown subscription provider 'nonexistent-provider'.
# Registered: ['claude-code', 'codex', 'gemini-cli', 'qwen-cli']I passed a bare model name and it silently billed my API key
Cause: Before PR #4147, auth= was dropped when llm= was a bare model name (or omitted), so requests went out on OPENAI_API_KEY. This is fixed — upgrade praisonai-agents to a version that includes the fix and every llm= shape honours auth=.
Mixing an OpenAI model with subscription auth
Behavior: Responses-API-eligible models (gpt-4o, o1, o3, openai/) with subscription auth now route to the subscription’s vendor. The simpler path is to omit llm= and let the auto-default pick the right vendor for your seat.
Token Detection Priority (Claude Pro)
Tokens are resolved in this exact order:
ANTHROPIC_TOKENenvironment variableCLAUDE_CODE_OAUTH_TOKENenvironment variable- macOS Keychain (
Claude Code-credentials) ~/.claude/.credentials.jsonfile
Environment Variable Override
You can disable subscription auth entirely (planned):
export PRAISONAI_DISABLE_SUBSCRIPTION_AUTH=1