Skip to main content
EchoZero supports OAuth 2.1 authorization code with PKCE for clients that cannot safely store long-lived API keys - Claude connectors, ChatGPT apps, local CLIs, and directory integrations.

Endpoints

StepMethodURL
Client lookupGEThttps://mcp.echozero.app/oauth/client
AuthorizeGEThttps://mcp.echozero.app/oauth/authorize
Approve (Dev Portal)POSThttps://mcp.echozero.app/oauth/authorize/approve
TokenPOSThttps://mcp.echozero.app/oauth/token
IntrospectPOSThttps://mcp.echozero.app/oauth/introspect
Verify statusGEThttps://mcp.echozero.app/status
User consent is handled in the Dev Portal at devportal.echozero.app/mcp/oauth/authorize. Users approve from their saved portal session - do not ask them to copy JWTs from browser devtools.

PKCE parameters

ParameterRequiredValue
response_typeYescode
client_idYesRegistered client id (e.g. echozero-cli)
redirect_uriYesMust match a registered redirect URI exactly
scopeYesSpace-delimited OAuth scopes (see below)
stateYesOpaque CSRF token - validate on callback
code_challengeYesBASE64URL(SHA256(code_verifier))
code_challenge_methodYesS256
Generate a code_verifier (43-128 random characters) and keep it secret until token exchange.
1

Redirect to authorize

Send the user to GET https://mcp.echozero.app/oauth/authorize with PKCE code_challenge, state, scope, and redirect_uri.
# Example authorize URL (build in your app)
https://mcp.echozero.app/oauth/authorize\
  ?response_type=code\
  &client_id=echozero-cli\
  &redirect_uri=http://127.0.0.1:8765/callback\
  &scope=read:agents%20read:wallet\
  &state=random-csrf-token\
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM\
  &code_challenge_method=S256
2

User approves in Dev Portal

EchoZero redirects to the Dev Portal consent page. If the user is not signed in, they authenticate first, then approve the requested scopes.
3

Receive authorization code

EchoZero redirects back to your redirect_uri with ?code=...&state=.... Verify state matches what you sent.
4

Exchange code for token

curl -sS -X POST https://mcp.echozero.app/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "authorization_code",
    "code": "<authorization_code>",
    "redirect_uri": "http://127.0.0.1:8765/callback",
    "client_id": "echozero-cli",
    "code_verifier": "<your_code_verifier>"
  }'
Response:
{
  "access_token": "ez_oauth_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read:agents read:wallet"
}
5

Call protected APIs

Send Authorization: Bearer <access_token> on REST and MCP requests.

Verify the token

After login, confirm the token is active:
curl -sS https://mcp.echozero.app/status \
  -H "Authorization: Bearer $OAUTH_TOKEN"
For server-side validation without calling user-scoped routes:
curl -sS -X POST https://mcp.echozero.app/oauth/introspect \
  -H "Content-Type: application/json" \
  -d '{ "token": "'"$OAUTH_TOKEN"'" }'
Returns { "active": true, "sub": "...", "client_id": "...", "scope": "...", "exp": ... } when valid.

OAuth scopes

Scopes map to API key permissions. Request only what your integration needs:
ScopeAccess
read:agents / write:agentsList and manage developer agents
read:strategies / write:strategiesStrategy configuration
read:wallet / write:walletWallet balances and mode
read:trades / trades:executeTrade history and execution
read:tokensToken metadata
read:earnEarnings and claims
read:analyticsDashboard analytics
read:ai / write:aiAI chat and tooling
read:notifications / write:notificationsNotification preferences
Legacy scope aliases (agents:read, bots:write, etc.) are also accepted. admin:* is not available via OAuth.

Token lifetime and refresh

TokenTTLNotes
Authorization code5 minutesSingle use; exchanged immediately
Access token1 hour (expires_in: 3600)No refresh_token is issued today
When the access token expires, re-run the PKCE consent flow or use a long-lived API key for headless servers. There is no offline_access or refresh grant at this time.

code_verifier requirements

43-128 characters from [A-Z], [a-z], [0-9], ., -, _, ~ (unreserved URI characters).

OAuth error responses

ConditionHTTPMessage / behavior
Expired or reused code400Invalid or expired authorization code
PKCE mismatch400Invalid code_verifier
Wrong redirect_uri400Redirect URI mismatch
Invalid client_id400Unknown client
Invalid state on callbackYour app should reject the callback

MCP with OAuth

OAuth tokens work on MCP the same way as API keys:
  1. POST https://mcp.echozero.app/mcp with Authorization: Bearer <access_token> on every request, including initialize.
  2. Save the mcp-session-id response header from initialize.
  3. Send mcp-session-id: <session> on subsequent MCP calls (tools/list, tools/call, SSE stream, etc.).
MCP sessions are bound to the credential used at initialize. Switching API keys or OAuth tokens while reusing the same mcp-session-id returns 403 with JSON-RPC error code -32002.
See the MCP Server guide for a full initialize example.

Assistant setup (coming soon)

Coming soon: The public skills pack (EchoZeroApp/skills) and ez login CLI are not yet published. Use the PKCE flow above or API keys until the skills repo is public.
# When available:
npx skills add EchoZeroApp/skills
ez login

Directory integrations

For ChatGPT, Claude, Gemini, and other assistant directories, use connector metadata from:
https://mcp.echozero.app/public/index.json
Per-platform manifests live under /public/directory/* with authorize URL, token URL, and recommended scope lists.