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:
| Path | Use it when | Result |
|---|---|---|
| Hosted MCP OAuth | A person already has a console account and the MCP client supports remote OAuth | An explicitly consented Personal assistant or Dedicated agent connection with selected resource reach and actions |
| Local CLI/stdio OAuth | An existing account should authorize a local agent or terminal | A separately consented API connection saved in the selected local profile |
| Enrollment key | Setup needs to create the agent | A new or existing agent identity plus a project-bound agent key |
| API key | The agent already exists | A project-bound key for that agent |
| Independent connection credential | An authorized full-control connection issues separate access for a worker | Its 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.
Local CLI and stdio sign-in
Section titled “Local CLI and stdio sign-in”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:
export EXTROVERT_PROFILE=supportnpx --yes --prefer-online @extrovert.dev/mcp auth loginnpx --yes --prefer-online @extrovert.dev/mcp whoamiauth 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.
Non-interactive login
Section titled “Non-interactive login”Start a pending login without launching a browser:
npx --yes --prefer-online @extrovert.dev/mcp auth login --no-browser --jsonIf 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:
npx --yes --prefer-online @extrovert.dev/mcp auth complete --jsonSupply 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.
Redeem an enrollment key
Section titled “Redeem an enrollment key”Redemption is idempotent on agent_handle. Reusing the same stable handle returns the same agent
instead of creating duplicates.
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\"}"import { Extrovert } from "@extrovert.dev/sdk";
const bootstrap = new Extrovert({ apiKey: process.env.EXTROVERT_ENROLLMENT_KEY! });const { client, enrollment } = await bootstrap.enrolled({ token: process.env.EXTROVERT_ENROLLMENT_KEY!, agent_handle: "support-agent", client_id: "enroll-support-agent",});
// Save enrollment.agent_key in your secret manager; never print the one-time secret.console.log((await client.whoami()).summary);{ "name": "redeem_enrollment", "arguments": { "enrollment_token": "pk_enroll_...", "agent_handle": "support-agent" }}Use the returned agent key as a bearer token:
Authorization: Bearer pk_agent_...Key reach is fixed
Section titled “Key reach is fixed”An agent key has a ceiling that cannot be widened after issuance.
| Ceiling | Prefix | Reach |
|---|---|---|
| Organization | pk_agent_org_... | The bound organization and allowed descendants; console or admin issuance only |
| Project | pk_agent_... | One project; this is the normal enrollment and self-signup result |
| Inbox | pk_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.
Permissions
Section titled “Permissions”The product calls the resource an inbox. Existing mailbox:* strings remain unchanged because
they are part of the public wire contract.
| Permission | Allows |
|---|---|
mailbox:create | Create inboxes and perform owning-agent lifecycle cleanup |
mailbox:read | Read inboxes, messages, threads, rules, review events, suppressions, and contact lists |
mailbox:send | Submit send, reply, forward, review chat, and revision requests |
mailbox:delete | Permanently delete inboxes |
mailbox:credentials | Export portable IMAP and SMTP credentials on an eligible paid plan |
mailbox:quota | Change an inbox’s rolling 24-hour recipient limit |
webhook:write | Create, update, and delete webhooks; older keys may use the documented read fallback |
domain:manage | Onboard, verify, inspect, and offboard customer-controlled domains |
commerce:request | Quote and request a domain purchase or plan change; never approve or authorize spending |
review:act | Act as a linked Review Loop reviewer; the link is also required |
signup:verify | Verify a self-signup code only |
account:admin | Explicit 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.
Hosted MCP OAuth
Section titled “Hosted MCP OAuth”Connect an OAuth-capable client to:
https://mcp.extrovert.dev/mcpThe 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.
Resume pending signup
Section titled “Resume pending signup”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.
Free self-signup
Section titled “Free self-signup”When self-signup and incoming-email activation are enabled:
- Call
POST /v1/agent/sign-upwith the human’s email and the requested inbox username. Setdisplay_namefor the friendly sender name, such asColeman; it is separate from the address. The response reserves an@free.extrovertmail.cominbox for 24 hours and returns a temporary key with onlysignup:verify. Keep this key; repeating signup does not rotate it or extend the reservation. - 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. - After showing those instructions, wait on
GET /v1/agent/activation?wait_seconds=55(check_activationwithwait_seconds: 55in MCP). A matching, authenticated incoming message changes the state frompendingtoproven, 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. - Call
POST /v1/agent/verifywith{}(verify_signupwithout an OTP). This revokes the temporary key and returns a durable key, the activated address,mailbox_quickstartinstructions, and anonboardinghandoff with the sender name, plan, account-aware console link andstarterreview ID, state, and optional coaching prompt. - Check
whoamiin the actual connection. Recover the existingsignup_starterreview 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.
Storage and revocation
Section titled “Storage and revocation”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.jsonor$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.
Common failures
Section titled “Common failures”| Status or code | Meaning | Next action |
|---|---|---|
401 | The credential is missing, expired, revoked, or invalid | Replace or reconfigure the exact credential |
403 forbidden_scope | The key is valid but lacks a required permission | Issue a narrower-purpose key with the required permission |
403 signup_disabled | Free self-signup or pending verification is paused | Use an enrollment key or wait for the gate to reopen |
404 | The resource is outside the credential’s granted reach, or does not exist | Verify whoami, project binding, and the opaque resource id |
409 quota_exceeded or 403 quota_exceeded | A lifetime or plan limit prevents creation | Inspect the returned problem details and issue a new key or change plan capacity as appropriate |