Add a Claude account
Adding an account Omniscio uses to talk to Anthropic: the Log In with Anthropic OAuth flow, cancelling a sign-in that is in progress, the single API key with its replace and re-enter paths, activating an account, and what the Settings page and the OAuth flow do under the hood.
What it is
An account in Omniscio is a credential Omniscio uses to talk to Anthropic's API — either a Claude.ai subscription (login via OAuth) or a raw sk-ant-... API key. Sessions need an account to spawn the Claude CLI; AI features (titles, suggestions, spam filtering, automations) also need one. Omniscio supports holding more than one account at the same time and silently picks the freshest one per spawn (see account-pool.md). The first account is added during onboarding; subsequent accounts are added from Settings → Accounts, which is the first item in the Settings sidebar.
There are two kinds of accounts. Login accounts sign in through the same PKCE OAuth flow as the Claude Code CLI — your Claude.ai subscription powers sessions, and Omniscio tracks 5-hour and 7-day usage caps per account. API key accounts use a raw key from the Anthropic Console; they pay per-token instead of consuming a subscription quota, and by default they are restricted to AI features only (the toggle that lets them spawn sessions is off out of the box).
Where to find it
How to use it
Open Settings → Accounts. Click the gear icon in the toolbar (or open the Settings virtual project from the Omniscio sidebar group). The first sidebar item is Accounts (User icon). The page lists every account you've already added under Login Accounts and API Keys, plus an Add Account card at the bottom.
Sign in with Claude (OAuth — primary path). In the Add Account card, click the prominent Log In with Anthropic button (LogIn icon, accent color). Omniscio opens your default browser to the Anthropic authorization page, you sign into Claude.ai, and you click Authorize on the consent screen. Omniscio's local callback server captures the authorization code, exchanges it for a refresh token, fetches your profile (email, display name, plan tier), and saves the account row. Total time is usually 10–20 seconds; the Add Account card shows a "Logging in..." state and a "try again" link below it that reopens the browser if the page didn't load. The login times out after 15 minutes.
- On success, a toast confirms
Logged in as <email>(orLogged in as <email> — now activeif Omniscio switched the active account to the new one). The account lands in the Login Accounts list with a green dot if it's now the active account. - Changed your mind? Cancel it. Next to "try again" is a cancel sign-in link (and a cancel action in the toolbar account popover's footer). Closing the browser tab does not tell Omniscio anything on its own — so without cancelling, Omniscio keeps believing a sign-in is in progress for the full 15 minutes, and during that time the Log In with Anthropic button stays disabled on "Logging in..." and a fresh attempt is refused with "A login is already in progress". Cancelling releases it immediately so you can start again or sign in as a different account. Cancelling saves nothing — an account row is only ever written after a successful sign-in.
- A sign-in that doesn't finish leaves no account behind. If a sign-in got far enough to receive tokens but Omniscio couldn't confirm which Claude account they belong to (a rate limit or a network blip at the wrong moment), the row is added unnamed and Omniscio keeps trying to identify it in the background. While that is happening the row reads "Finishing sign-in…"; if it can never be identified it reads "Sign-in didn't finish" with a plain-language note, offers no Switch (there is no account to switch to), and is cleaned up automatically. Historically these rows showed as
unknown@claude.aicomplete with a plan badge, looked like a second real account, and stayed forever — see add-a-claude-account §Related and the auth-login-contract for the invariant. - The OAuth scopes requested are
org:create_api_key,user:profile,user:inference,user:sessions:claude_code,user:mcp_servers, anduser:file_upload. The scopes match what the official Claude Code CLI requests so your Claude.ai subscription works the same way it does inclaudeon the terminal. - Re-authorizing an existing account targets the right identity automatically. When you click "Log In" on a specific account row, Omniscio passes that account's email as
login_hintin the OAuth URL so Anthropic's consent screen pre-selects the correct account — you don't need to manually switch accounts in your browser first. The existing account row's tokens are updated in place instead of creating a duplicate. If you sign in to a different Claude.ai account in the browser (overriding the hint), Omniscio creates a new row and (if it was your only account before) flips it to active.
- On success, a toast confirms
Or add an API key. Below the Log In button, click the small Or add an API key manually link (Key icon). A password-style input appears — paste your
sk-ant-...key from https://console.anthropic.com and click Add API Key. Omniscio saves the encrypted key to disk — it does not pre-validate the key, so use Check Limits on the row afterwards to confirm it works. A toast confirmsAPI key added. If the key is invalid you'll see aCould not add account: <reason>error and the input stays open.- The first API key shows a separate prominent card at the top of the API Keys section labeled Anthropic API Key with a three-state badge: green Configured (a saved key Omniscio can actually read), amber Can't be read — re-enter (the key record exists but its encrypted-at-rest copy no longer decrypts — typically after a hardware/Windows change resets DPAPI; the key itself is still fine), or grey Not configured (no key at all). The card's inline paste form appears whenever the badge is not green — so an unreadable key can no longer hide the only way to re-enter (that was the bug). Re-pasting the same key while it's amber repairs it in place (no duplicate row) and the badge returns to green. Use either entry point; both call the same handler. You're also notified proactively: if a saved key can't be read, Omniscio raises a persistent Inbox alert at startup — independent of other features, so it shows even with the waiting double-check turned off — pointing you to re-enter the key; re-entering clears it. Full semantics: apikey-badge-decrypt-health-contract.md.
- API key accounts cannot spawn sessions out of the box. The setting Allow API Keys to Run Sessions lives just below the API key list and is off by default. While off, API keys only power AI features — session spawn falls through to a login account or surfaces the all-exhausted banner if no login account has capacity. Flip the toggle on if you want sessions to run on the API key (you'll be billed per token by Anthropic). See account-pool.md for how the pool tier-list interacts with this setting.
- Replace a key in place. Each row in the API Keys list has a Replace link (alongside Activate and the trash icon). Click it to open a paste box right on that row, paste a new
sk-ant-...key, and click Replace key — the key is swapped on the same account in place (its name, cost history, and active status are kept), so you never have to add a second key and delete the old one, and the last-account removal guard never gets in the way. The swap is immediate and not pre-validated, so use Check Limits afterwards to confirm the new key works; a "Replaces your current key immediately" note sits under the box, and if the save fails the box stays open showing the error (it never falsely reports success). Replace shows only on a healthy key — an unreadable (amber) key keeps the re-enter it above flow on the prominent card instead. Mechanics: apikey-badge-decrypt-health-contract.md (B9).
(Optional) Pick which account is active. After adding an account, the row in the Login Accounts or API Keys list shows an Activate link if it isn't already the active one. Clicking Activate flips the active-account flag, which the account pool uses as the preferred account for the next spawn. There's no separate "Pin" affordance — Activate is the pin. Nothing about your existing sessions or projects is automatically reassigned to the new account; pinning is purely about who powers the next spawn.
Repeat for as many login accounts as you want. Omniscio has no cap on login accounts — the pool silently rotates through them based on usage headroom (see account-pool.md). Two common patterns: keep two Claude.ai subscriptions to double your daily session capacity, or keep a Claude.ai login as primary and a single API key as a metered fallback for AI features. API keys are the exception: Omniscio keeps exactly one. Adding an API key when one already exists replaces it in place instead of stacking a second, and a pile left over from before is collapsed to the one already in use the next time Omniscio starts — multiple keys serve no purpose (only one is ever used) and would otherwise let different AI features bill to different keys. Full rules: single-anthropic-apikey-contract.md.
How it behaves
How it works
The page is AccountSettings.tsx, which reads from the account-store.ts Zustand slice (the same store that powers the toolbar's AccountIndicator). The OAuth flow is implemented entirely in auth-service.ts — authService.login() is the single public entry point. It generates a PKCE code verifier + challenge, starts a local callback server on localhost:<random-port>, opens the Anthropic authorize URL via shell.openExternal(), waits for the redirect to /callback, and POSTs the authorization code to https://platform.claude.com/v1/oauth/token with Content-Type: application/json and a JSON.stringify'd body. The bind host is deliberately localhost and not 127.0.0.1 — Windows 11 resolves localhost to ::1, so binding the literal IPv4 address can miss the browser's callback (oauth-callback-server-core.ts). The token endpoint format is non-negotiable — see CLAUDE.md "Token endpoint format" rule. The exchange does not retry: it runs with maxRetries: 0, because retrying the token endpoint is forbidden by the auth-login-contract (exchange-must-succeed-on-first-try). The whole sign-in holds that endpoint, not just its HTTP round-trip: auth-login-flow.ts claims it on the gateway when the flow starts and releases it in its finally, and while it is held a background refresh is refused before it reaches the network — so it can neither spend the exchange's rate-limit budget nor arm the 429 backoff the exchange would then have to read. A background caller's 429 observed during that window does not arm that backoff either (auth-login-contract, login-exchange-gets-absolute-priority). Instead, when the exchange is rate-limited, a first-time login (no existing accounts) falls back to importing the Claude Code CLI's stored credentials. After the token exchange, fetchProfile() calls /api/oauth/profile to get the email, display name, and rate_limit_tier, and saveAccount() writes the account row into config.json. Renderer-side, the Log In with Anthropic button calls addLoginAccount() on the account store, which invokes IPC.ACCOUNT_ADD_LOGIN; the handler in claude-provider.ts parses an optional { loginHint } payload (validated by addLoginAccountSchema) and calls authService.login(loginHint). When re-authenticating an existing account row, the renderer passes account.email as the hint; when adding a brand new account (the "+ Log In" button), no hint is sent. authService.login() appends login_hint=<email> to the OAuth authorization URL when a hint is provided, so Anthropic's consent screen pre-selects the correct account. The handler returns { account, accountSwitched } so the toast can say "now active" when appropriate.
Identity resolution is the fragile step, and it is now bounded-retried rather than one-shot: resolveProfileWithRetry (profile-resolve-policy.ts) makes up to 3 profile attempts (~1.6s worst-case extra wait) before falling back to the unknown@claude.ai sentinel. That retry is safe because the profile API lives on api.anthropic.com, a different domain with independent rate limits from the token endpoint — it is deliberately not a retry of the token exchange, which the auth contract forbids. Any row that still lands on the sentinel is healed in the background by reresolveStaleAccountProfiles(); a row that can never be healed (its refresh token was quarantined, so the heal's ensureTokenValid gate can never pass) is removed by reapLoginAccountResidue (auth-account-residue-reaper.ts) at the end of that same pass. The reaper is deliberately conservative — a row must have an unresolved identity and provably dead credentials and be past a 10-minute grace window — and both it and the row's UI label read the same shared classifier (login-account-residue.ts), so what the list says can never disagree with what gets swept. Cancelling an in-flight login goes through IPC.ACCOUNT_CANCEL_LOGIN → authService.cancelLogin(), which rejects the pending login() so its existing finally releases the in-progress lock and closes the loopback callback server.
API key adds go through IPC.ACCOUNT_ADD_APIKEY validated against addApiKeyAccountSchema (only apiKey is required; name and email default to 'API Key' and ''). authService.addApiKey() enforces the single-key model: with no api-key account it creates one (new UUID) and auto-activates if there's no active account yet; with one already present it writes the key into the canonical slot (the active apikey, else the oldest) in place via replaceApiKey and removes any other api-key rows, so a second never accumulates. A one-time collapseApiKeyAccounts pass inside migrateAccountSystem (at config load, after decryption) likewise collapses any pre-existing pile to that single usable/in-use key — removing empty-junk rows and extras, but never the last re-enterable key (so a key that's only temporarily unreadable after a DPAPI reset still has a slot to repair). Full rules + tests: single-anthropic-apikey-contract.md. When a saved key can no longer be decrypted (F065, see the badge note above), the schema also accepts an optional replaceAccountId so re-entry routes to authService.replaceApiKey() — an in-place saveAccount on the existing id that repairs the resolved (active-apikey-else-first) account instead of minting a duplicate, and clears the F065 flag. Targeting the resolved account matters: the AI features read the active apikey first (resolveApiKeyCredential), so adding a new inactive key would leave the badge green while the double-check still reads the dead one — see apikey-badge-decrypt-health-contract.md (B7). Separately, each api-key row exposes a Replace action (handleReplaceApiKey) that sends ACCOUNT_ADD_APIKEY with the explicit clicked-row id as replaceAccountId — the same in-place replaceApiKey swap, but it never runs the F065 resolveApiKeyReplaceTarget fallback, so it carries none of that insertion-order coupling (B9). Both account types are stored in the same accounts[] array in config.json (a discriminated union on type: 'login' | 'apikey'), with credentials encrypted via Electron's safeStorage.encryptString() and tagged with the enc: prefix per the security rule "Credential encryption" in CLAUDE.md. Tokens never reach the renderer — the renderer only sees AccountSummary objects with credential fields stripped. At session spawn time, the process manager reads authService.getCredentials(accountId) and injects either CLAUDE_CODE_OAUTH_TOKEN (login) or ANTHROPIC_API_KEY (api key) into the spawned CLI's environment. The allowApiKeySessionSpawn toggle is enforced in three layers: filterAccountsForSpawn() in pickActiveAccount(), shouldBlockApiKeySpawn() in spawnLocalStreamJson(), and filterSpawnEligibleAccounts() in tryAutoSwitch() (see account-management.md "API Key Session Guard").
Related
- account-pool.md — once you have multiple accounts, the pool tier-list decides which one each session uses
- switch-active-account.md — there's no "switch active account" button per se; this page explains why and how Activate interacts with the pool
- remove-a-claude-account.md — taking an account back out, including the only-account case
- usage-forecast.md — the inline forecast row each login account row shows, predicting when its 5-hour bucket exhausts
Last verified 2026-09-29