Skip to content

Authentication and keys

For the shortest path, start at Set up your agent: agent handoff, existing-account connection, manual commands, and private scoped-key installation.

Choose authentication for the runtime and access for the task:

PathUse it whenResult
Hosted MCP OAuthA person already has a console account and the MCP client supports remote OAuthAn explicitly consented Personal assistant or Dedicated agent connection with selected resource reach and actions
Local CLI/stdio OAuthAn existing account should authorize a local agent or terminalA separately consented API connection saved in the selected local profile
Enrollment keySetup needs to create the agentA new or existing agent identity plus a project-bound agent key
API keyThe agent already existsA project-bound key for that agent
Independent connection credentialAn authorized full-control connection issues separate access for a workerIts own reach, actions, lifetime, and revocation; survives the issuer ending

Use explicit connection consent to delegate customer administration. Keep the human sign-in session private; it is not the credential to give an agent.

For the setup-to-worker workflow, resource choices, the 24-hour full-control default, and separate revocation, read Connections and access.

Use your existing account. The packaged CLI can sign in without asking you to create an enrollment key. Select the intended profile before every command; skip the export to keep your current profile:

Terminal window
export EXTROVERT_PROFILE=support
npx --yes --prefer-online @extrovert.dev/mcp auth login
npx --yes --prefer-online @extrovert.dev/mcp whoami

auth login first verifies and reuses working access in that profile. If it cannot verify existing access, it preserves the credential and reports the failure. Use --reconnect only when you intend new sign-in and consent; use a different profile for another agent.

In a local interactive terminal, login opens a browser when available and finishes automatically when the browser returns to that terminal. The printed fallback link returns to Extrovert’s website, so you can use it on another machine and paste its one-use completion code at the hidden terminal prompt. SSH, CI, and sessions without a local browser use this hosted completion path directly. --no-browser chooses it explicitly; --browser requests local browser login from an interactive terminal.

Start a pending login without launching a browser:

Terminal window
npx --yes --prefer-online @extrovert.dev/mcp auth login --no-browser --json

If status is pending, show authorization_url to the person. They sign in to the existing account, choose access, and copy the one-use completion code from Extrovert’s completion page. Resume in the same profile:

Terminal window
npx --yes --prefer-online @extrovert.dev/mcp auth complete --json

Supply the completion code through private stdin; an interactive invocation offers a hidden prompt. Never put the code in a command argument, chat, or log. If this agent cannot accept private stdin, ask the person to run auth complete --json at the hidden terminal prompt in the same profile; keep the agent connection pending until that finishes. The completion page does not itself connect the agent: wait for status: complete, then run whoami. Login requests expire after 10 minutes; a declined or expired attempt needs a new login. A malformed code can be supplied again while the same attempt remains pending. auth cancel clears pending login state and preserves existing access; it does not revoke an established connection. auth logout revokes the stored local OAuth connection before removing its credentials. If revocation cannot be confirmed, the CLI preserves them so logout can be retried. Independently issued API keys require separate revocation in Connections; removing their local copy does not revoke them.

The CLI saves OAuth credentials privately for its selected profile and refreshes them for local CLI/stdio use. Refresh preserves the consented identity, resource reach, actions, and original grant expiry. After login, start or reload the matching stdio MCP process and call whoami in that session. The MCP host’s hosted OAuth connection is separate: local login does not configure, refresh, or test it.

EXTROVERT_PROFILE isolates local identities. Hermes uses its selected HERMES_HOME; EXTROVERT_CONFIG_DIR explicitly chooses the credential directory. EXTROVERT_API_KEY takes precedence over stored access. If it is set, check which credential you intend to use; new browser consent requires removing that explicit override from the chosen environment. Keep the same profile and API environment throughout a pending login. Production uses https://api.extrovert.dev.

Use the existing installation manager and deliberate package pins. The unpinned examples above request fresh registry metadata; see updates before changing an installation.

Enrollment keys and API keys are different

Section titled “Enrollment keys and API keys are different”

The console separates the two under Credentials:

  • Enrollment keys bootstrap agents. They may be reusable or single-use, expire independently, and can limit permissions, allowed domains, lifetime inbox creation, and daily sending.
  • API keys connect software to an existing active agent. They do not create an agent.

Both raw secrets are shown once. Store them in a secret manager or the packaged MCP credential store.

Redemption is idempotent on agent_handle. Reusing the same stable handle returns the same agent instead of creating duplicates.

Terminal window
export EXTROVERT_API_BASE_URL="https://api.extrovert.dev"
export EXTROVERT_ENROLLMENT_KEY="pk_enroll_..."
curl -sS -X POST "$EXTROVERT_API_BASE_URL/v1/enroll" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: enroll-support-agent" \
-d "{\"token\":\"$EXTROVERT_ENROLLMENT_KEY\",\"agent_handle\":\"support-agent\"}"

Use the returned agent key as a bearer token:

Authorization: Bearer pk_agent_...

An agent key has a ceiling that cannot be widened after issuance.

CeilingPrefixReach
Organizationpk_agent_org_...The bound organization and allowed descendants; console or admin issuance only
Projectpk_agent_...One project; this is the normal enrollment and self-signup result
Inboxpk_agent_inbox_...One inbox in one project; console or admin issuance only

pk_agent_proj_... remains accepted as a compatibility form for project keys. A project id in a request is an assertion, not a selector. If it does not match the key’s project, the request fails.

Call GET /v1/auth/me, client.whoami(), or whoami after connecting. Record the fixed org_id, project_id, agent id, key id, and permissions before doing work.

The product calls the resource an inbox. Existing mailbox:* strings remain unchanged because they are part of the public wire contract.

PermissionAllows
mailbox:createCreate inboxes and perform owning-agent lifecycle cleanup
mailbox:readRead inboxes, messages, threads, rules, review events, suppressions, and contact lists
mailbox:sendSubmit send, reply, forward, review chat, and revision requests
mailbox:deletePermanently delete inboxes
mailbox:credentialsExport portable IMAP and SMTP credentials on an eligible paid plan
mailbox:quotaChange an inbox’s rolling 24-hour recipient limit
webhook:writeCreate, update, and delete webhooks; older keys may use the documented read fallback
domain:manageOnboard, verify, inspect, and offboard customer-controlled domains
commerce:requestQuote and request a domain purchase or plan change; never approve or authorize spending
review:actAct as a linked Review Loop reviewer; the link is also required
signup:verifyVerify a self-signup code only
account:adminExplicit full customer administration within the current authorizing person’s roles; excludes private platform access

domain:purchase is a legacy compatibility permission. New agent credentials should use commerce:request for human-controlled purchase requests.

Connect an OAuth-capable client to:

https://mcp.extrovert.dev/mcp

The client discovers the authorization server, opens browser sign-in and consent, then stores and refreshes the grant. No Extrovert key belongs in the client configuration.

Choose Personal assistant or an explicitly selected or created Dedicated agent, then Selected inboxes, Project, Organization, or Full account control. Actions are separate from resource reach. Selected inboxes use stable IDs and exclude future inboxes; project and organization reach includes future resources in that boundary. Current human authority remains the upper bound.

Full account control enables customer administration, including purchases, policies, access changes, and issuing credentials. It defaults to 24 hours, with an explicit Until revoked option. Refresh never extends the original expiry. Independently created credentials, including administrative credentials, survive and require separate revocation. Manage these in Connections.

Existing hosted OAuth connections must reconnect to grant explicit consent. Hosted MCP also accepts existing scoped agent keys. Independently issued ev_credential_... credentials work with the API and packaged local stdio/CLI, not as hosted MCP bearer tokens. Hosted MCP rejects enrollment keys. See the MCP access model.

Use the existing reservation, credential and profile. Missing proof is not permission to start another signup or switch the pending profile to OAuth or auth login. Show the human the returned verification instructions before waiting. After verification, check fresh identity and recover the exact automatically prepared practice review.

Keep the two activation methods separate. incoming_email uses the returned activation record; an address correction requires its current revision. A legacy OTP reservation instead uses the issued code with verify_signup. Do not invent an incoming activation record or revision, or call correct_activation_email, to repair a missing legacy OTP email. Confirm its returned destination and check Spam or Junk; preserve the reservation and report any unsupported recovery step.

For a locally saved legacy signup, the person can run extrovert verify in their own private, unrecorded terminal using the same profile. Its OTP prompt is visible, not hidden. This command verifies and saves the replacement credential itself; it does not pass the code to a later MCP call. Do not request codes in chat or invent a private input channel. After a successful CLI verification, an MCP session still showing limited access may use another profile, an environment-key override, or stale connection state. Check that connection before repeating verification or declaring the account unverified. Local success does not establish the active MCP session’s identity.

When self-signup and incoming-email activation are enabled:

  1. Call POST /v1/agent/sign-up with the human’s email and the requested inbox username. Set display_name for the friendly sender name, such as Coleman; it is separate from the address. The response reserves an @free.extrovertmail.com inbox for 24 hours and returns a temporary key with only signup:verify. Keep this key; repeating signup does not rotate it or extend the reservation.
  2. Tell the human: Your agent’s inbox is almost ready. Send an email from {human_email} to {address} to activate it and link it to your human email. Any subject or body is fine.
  3. After showing those instructions, wait on GET /v1/agent/activation?wait_seconds=55 (check_activation with wait_seconds: 55 in MCP). A matching, authenticated incoming message changes the state from pending to proven, establishing ownership. Extrovert queues a welcome and one practice draft immediately, even if the agent is offline. A different sender cannot take ownership. Repeat pending waits for up to five minutes; preserve the reservation on timeout. The packaged CLI does this automatically during signup.
  4. Call POST /v1/agent/verify with {} (verify_signup without an OTP). This revokes the temporary key and returns a durable key, the activated address, mailbox_quickstart instructions, and an onboarding handoff with the sender name, plan, account-aware console link and starter review ID, state, and optional coaching prompt.
  5. Check whoami in the actual connection. Recover the existing signup_starter review and feedback; do not send a second hello. Continue with the first-message guidance in your first conversation.

The pending key cannot read or send mail, create resources, export credentials, or configure forwarding and webhooks. The human can instead sign up (or sign in if already registered) with a verified matching email and explicitly approve the pending account in the console. Existing account owners enroll additional agents through their console.

For incoming_email before ownership proof, to correct an address, call PATCH /v1/agent/activation with human_email and the current returned revision (correct_activation_email in MCP). Correction requires a new matching message and does not extend the 24-hour deadline.

For a legacy signup response that issued an OTP, complete verification before its original expiry. A saved pending signup is not an authenticated CLI connection: whoami reports that verification is needed, not the reservation’s identity. Resume verify before checking identity or requiring MCP setup. If the signup is saved in the packaged local profile, the person can run extrovert verify in their own private, unrecorded terminal using that same profile. Its verification-code input is visible, not masked; the hidden auth complete prompt above belongs to a different login method. Do not put the OTP in chat or command arguments. verify completes verification and saves the replacement credential itself: it does not transfer the code to MCP, and no second verify_signup call is needed. Check whoami in the agent’s actual connection afterward. Another profile, hosted OAuth connection, or explicit environment key does not automatically adopt this saved credential. If the MCP still reports limited signup access after the CLI succeeded, inspect the selected profile, explicit key override and connection reload state first; that result alone does not prove account verification failed. Do not repeat a completed exchange or create another account to repair the connection.

Alternatively, verify_signup accepts an OTP through an already available, private host input channel. Do not invent such a channel or assume a terminal prompt provides it. If neither private handoff is available, preserve the pending signup and explain the limitation.

Missing verification mail is not an incoming-email activation record: do not invent a revision or call correct_activation_email for that reservation. Confirm the returned destination and check Spam or Junk. Use only recovery actions supported for the returned method; preserve the pending account rather than starting another signup.

Use an existing account first. Unattended workers must receive the human email through authorized setup; never invent an address or bypass verification. Retain pending state until the human completes activation.

The packaged local MCP server stores local OAuth credentials, durable agent keys, or independently created connection credentials in a permission-restricted credential file:

  • Linux and macOS: ~/.config/extrovert/credentials.json or $XDG_CONFIG_HOME/extrovert/credentials.json
  • Windows: %APPDATA%\Extrovert\credentials.json

EXTROVERT_API_KEY is an explicit override. Keep secrets out of source files, shell history, logs, screenshots, and support bundles. Revoke a key that may have been exposed.

Revoking an enrollment key prevents future redemption. It does not revoke agent keys already issued from it. Revoke those keys or disable the agent separately when needed.

Status or codeMeaningNext action
401The credential is missing, expired, revoked, or invalidReplace or reconfigure the exact credential
403 forbidden_scopeThe key is valid but lacks a required permissionIssue a narrower-purpose key with the required permission
403 signup_disabledFree self-signup or pending verification is pausedUse an enrollment key or wait for the gate to reopen
404The resource is outside the credential’s granted reach, or does not existVerify whoami, project binding, and the opaque resource id
409 quota_exceeded or 403 quota_exceededA lifetime or plan limit prevents creationInspect the returned problem details and issue a new key or change plan capacity as appropriate