---
name: get-api-key
description: Obtain an Anthropic API key by calling the Console's internal API endpoints via browser_evaluate, falling back to screenshot-based browser automation if direct API calls fail.
---

# Get Anthropic API Key

Obtain an API key from the Anthropic Console (platform.claude.com) on behalf of the user. The personal-assistant calls Console API endpoints directly via `browser_evaluate` + `fetch()` — the browser's session cookies handle auth automatically. This is orders of magnitude faster than navigating pages and interpreting screenshots.

**Separate sign-in expected.** The Anthropic Console (`platform.claude.com`) and Claude (`claude.ai`) are separate services with independent sessions and billing. Users who just authenticated via OAuth on `claude.ai` will not have a session on the Console — a second sign-in is required. The same email and password (or Google account) work on both. When the flow requires sign-in, explain this proactively so the user understands why they are signing in again.

**Critical ordering rule:** Credits must be confirmed on the account before creating an API key. Keys created on a zero-balance account are permanently rejected by the Anthropic API — even after credits are added later.

**Auth model:** The Console uses an `httpOnly` session cookie (`sessionKey`) set during sign-in. Because it's `httpOnly`, JavaScript can't read it — but `fetch()` with `credentials: 'include'` sends it automatically. All API calls in the briefs below rely on this.

## Steps

1. **Show the browser.** Call `render-component` with:
   - `name: "browser-viewer"`
   - `data: { title: "Anthropic Console" }`

2. **Dispatch personal-assistant for sign-in check and billing assessment.** Use the Agent tool with `specialists:personal-assistant` as the subagent type. Brief:

   > Navigate to https://platform.claude.com/settings/billing. Wait for the page to load.
   >
   > Then use `browser_evaluate` to run this JavaScript:
   > ```js
   > (async () => {
   >   try {
   >     const orgsRes = await fetch('/api/organizations', { credentials: 'include', headers: { 'content-type': 'application/json' } });
   >     if (orgsRes.status === 401 || orgsRes.status === 403) return { status: 'SIGN_IN_REQUIRED' };
   >     const orgs = await orgsRes.json();
   >     const apiOrg = orgs.find(o => o.capabilities && o.capabilities.includes('api'));
   >     if (!apiOrg) return { status: 'NO_API_ORG', orgs: orgs.map(o => ({ name: o.name, capabilities: o.capabilities })) };
   >     const creditsRes = await fetch(`/api/organizations/${apiOrg.uuid}/prepaid/credits`, { credentials: 'include', headers: { 'content-type': 'application/json' } });
   >     if (!creditsRes.ok) return { status: 'BILLING_CHECK_FAILED', httpStatus: creditsRes.status };
   >     const credits = await creditsRes.json();
   >     return { status: 'OK', orgId: apiOrg.uuid, orgName: apiOrg.name, creditsCents: credits.amount, currency: credits.currency };
   >   } catch (e) { return { status: 'FETCH_ERROR', error: e.message }; }
   > })()
   > ```
   >
   > Report the result object exactly as returned. Do not interpret or summarize — pass the JSON back verbatim.

3. **Handle the billing result.**
   - `status: "OK"` with `creditsCents > 0`: Credits available. Note the `orgId` — it's needed for key creation. Proceed to step 6.
   - `status: "OK"` with `creditsCents === 0`: No credits. Proceed to step 5.
   - `status: "SIGN_IN_REQUIRED"`: Proceed to step 4.
   - `status: "NO_API_ORG"`: Tell the user: "Your Anthropic account doesn't have an API organization. Visit platform.claude.com and set up API access, then try again."
   - `status: "BILLING_CHECK_FAILED"` or `status: "FETCH_ERROR"`: The direct API call failed. Fall back to the screenshot-based billing check (step 2F).

4. **Sign-in required.** Explain the two-account situation, then offer to continue or skip. Tell the user:

   "You'll need to sign in to the Anthropic Console in the browser above. This is a separate sign-in from the one you did earlier — Anthropic treats Claude (claude.ai) and the API Console (platform.claude.com) as independent services with separate sessions and separate billing, even though they use the same credentials. Use the same email and password (or Google account) you used before.

   You can use Google sign-in or enter your email — Anthropic will send a verification code. If the verification email opens on this device, open it in a new tab in that same browser (not on your phone or another device) so the session stays connected.

   If you don't have an Anthropic Console account yet, create one at platform.claude.com — you'll need to add credits before a key can be generated.

   This step is optional — if you'd rather skip it for now, just say so and we'll move on. You can set up the API key any time."

   If the user skips, return control to the caller without storing a key. If the user confirms they have signed in, return to step 2 to re-check billing.

5. **No credits — guide the user.** Tell the user: "Your Anthropic account needs credits loaded before I can create an API key. Add credits or enable auto-top-up on the billing page. Let me know when you're done." Wait for confirmation, then re-dispatch the billing check (step 2). If credits are now present, proceed to step 6. If still no credits, repeat this step (maximum 3 attempts). After 3 attempts with no credits detected, tell the user: "Credits still not detected. You can add them at platform.claude.com/settings/billing and then ask me to create the API key again."

6. **Dispatch personal-assistant for key creation.** Use the Agent tool with `specialists:personal-assistant` as the subagent type. Include the `orgId` from step 3. Brief:

   > Use `browser_evaluate` to run this JavaScript (replace ORG_ID with the orgId from the billing check):
   > ```js
   > (async () => {
   >   try {
   >     const res = await fetch('/api/console/organizations/ORG_ID/workspaces/default/api_keys', {
   >       method: 'POST',
   >       credentials: 'include',
   >       headers: { 'content-type': 'application/json' },
   >       body: JSON.stringify({ name: 'Maxy' })
   >     });
   >     if (res.status === 401 || res.status === 403) return { status: 'SIGN_IN_REQUIRED' };
   >     if (!res.ok) return { status: 'CREATE_FAILED', httpStatus: res.status, body: await res.text() };
   >     const key = await res.json();
   >     if (!key.raw_key) return { status: 'NO_RAW_KEY', keyId: key.id };
   >     return { status: 'KEY_CREATED', rawKey: key.raw_key, keyName: key.name, keyId: key.id };
   >   } catch (e) { return { status: 'FETCH_ERROR', error: e.message }; }
   > })()
   > ```
   >
   > If the result has `status: "KEY_CREATED"`, call `api-key-store` with the `rawKey` value. Report the full result object back.
   >
   > If the result has any other status, report it back verbatim — do not attempt to navigate the Console UI.

7. **Handle the key creation result.**
   - `status: "KEY_CREATED"` and specialist confirms key was stored: proceed to step 9 (verify).
   - `status: "SIGN_IN_REQUIRED"`: this is unexpected since sign-in was handled during the billing check. Tell the user to sign in, wait for confirmation, then retry step 6 once. If it fails again, proceed to step 8 (fallback).
   - `status: "CREATE_FAILED"` or `status: "FETCH_ERROR"`: The direct API call failed. Fall back to the screenshot-based key creation (step 6F).
   - `status: "NO_RAW_KEY"`: The key was created but the response didn't include the full key value. Proceed to step 8 (manual fallback).

8. **Fallback.** Tell the user: "I could not extract the key automatically. Please copy it from the browser and paste it here in the chat." When the user pastes a key starting with `sk-ant-`, call `api-key-store` with it. Then proceed to step 9.

9. **Verify the key works.** Call the `api-key-verify` MCP tool. Handle each status:
   - `valid`: Confirm to the user — "API key stored and verified. Your public agent is ready."
   - `billing`: Credits were confirmed before key creation, so this is unexpected. Tell the user: "The key was stored but credits may not have propagated yet. Please wait a moment." Retry `api-key-verify` once after a brief pause. If still `billing`, tell the user: "Credits are not being detected yet. The key is stored — the public agent will start working once Anthropic recognises the credits on your account."
   - `auth_error`: The key was rejected. Tell the user: "The stored key was rejected by Anthropic. It may have been copied incorrectly. Let's try again." Return to step 6 (maximum 2 retries — if it fails 3 times total, tell the user to visit platform.claude.com/settings/keys in their own browser and paste the key manually).
   - `error`: Could not verify (network or Anthropic issue). Tell the user: "I couldn't verify the key right now — this is likely a temporary issue. The key is stored and should work once the connection is restored."

## Fallback Steps (screenshot-based)

These steps are used when `browser_evaluate` + `fetch()` fails. They replicate the original browser automation approach.

### Step 2F: Screenshot-based billing check

Dispatch personal-assistant with brief:

> Navigate to https://platform.claude.com/settings/billing. Take a screenshot to assess the page. Report what you see: Is there a credit balance shown? Does it say "Buy credits to get started"? Is a sign-in page shown instead? Report the billing status and any dollar amount visible. If sign-in is required, report "SIGN_IN_REQUIRED" and stop.

Handle the result the same as step 3, mapping the specialist's text observations to the structured statuses.

### Step 6F: Screenshot-based key creation

Dispatch personal-assistant with brief:

> Navigate to https://platform.claude.com/settings/keys. Take a screenshot to verify you are on the API Keys page. Create a new API key named "Maxy": find and click the "Create Key" button, fill the key name with "Maxy", submit the form. After creation, look for a string starting with sk-ant- on the page — Anthropic shows the key only once. If found, call api-key-store with the key. Report what happened. If sign-in is required, report "SIGN_IN_REQUIRED" and stop.

Handle the result the same as step 7.

## Important

- **`render-component` is required before any personal-assistant dispatch.** The browser-viewer component is what makes the VNC browser visible to the user. Without it, automation runs invisibly. Always call `render-component` with `name: "browser-viewer"` and confirm it renders before dispatching a personal-assistant subagent.
- **Credits before keys.** Never create an API key before confirming that credits are loaded on the account. A key created on a zero-balance account is permanently non-functional.
- The browser is visible to the user via VNC. They can see everything the specialist does.
- The user must sign in themselves — the specialist does not fill credentials.
- For email verification: tell the user to open the verification email in a new tab in the same VNC browser (not on their own device). This keeps the sign-in session in the same browser context.
- Anthropic shows the API key only once after creation. With the direct API approach, the `raw_key` is in the JSON response — but if the specialist fails to call `api-key-store` before its context ends, the key is lost and a new one must be created.
- If the personal-assistant is not available (not installed, Agent tool fails), skip the browser-viewer component entirely and tell the user to get a key from platform.claude.com in their own browser, then paste it in the chat.
- The Console URL `console.anthropic.com` redirects to `platform.claude.com`. All briefs use `platform.claude.com` to avoid redirect latency.

## API Reference

For the complete captured API surface (endpoint details, response schemas, auth mechanism), see `references/console-api.md`.
