Every request to a CharmIQ MCP server carries a bearer token. The token is issued by CharmIQ's OAuth 2.1 server, scoped to a single Organization, and tied to the set of permissions the user granted. The whole flow is standards-compliant — any MCP client that supports discovery will configure itself automatically.This page is the developer reference. If you're connecting an off-the-shelf client (Claude Code, VS Code, Zapier), use Connecting External Tools to CharmIQ instead.Discovery Endpoints
CharmIQ implements RFC 8414 (Authorization Server Metadata) and RFC 9728 (Protected Resource Metadata). Point your client at the well-known URLs and it learns the rest.| Endpoint | URL |
|---|
| Authorization Server Metadata | https://api.charmiq.ai/.well-known/oauth-authorization-server |
| Protected Resource Metadata | https://api.charmiq.ai/.well-known/oauth-protected-resource |
| Authorization | https://api.charmiq.ai/oauth/authorize |
| Token | https://api.charmiq.ai/oauth/token |
| Dynamic Client Registration (RFC 7591) | https://api.charmiq.ai/oauth/register |
| User Info | https://api.charmiq.ai/oauth/userinfo |
The OAuth "resource" is https://api.charmiq.ai/mcp. The token issued from this server works against every sub-server (/mcp/vfs, /mcp/agent, etc.) — you do not authorize per server.The Flow
CharmIQ supports two grant types: authorization_code and refresh_token. PKCE is mandatory for every authorization request — S256 only, not plain.The standard sequence for a public client:- Fetch authorization server metadata.
- Register the client (RFC 7591). Dynamic clients are public, no secret, PKCE-only. The result includes a
client_id you reuse on subsequent flows. - Generate a PKCE pair (
code_verifier, code_challenge). - Redirect the user to
/oauth/authorize with response_type=code, your client_id, your redirect_uri, the scope you want, and the code_challenge. - The user picks an Organization and approves the scopes.
- CharmIQ redirects back to your
redirect_uri with a code. - Exchange the code at
/oauth/token (sending the code_verifier) for an access token, refresh token, and ID token.
The access token is a JWT carrying:| Claim | Meaning |
|---|
sub | The CharmIQ user |
org | The Organization the user selected during consent |
scope | The space-delimited scopes that were actually granted |
Decode the JWT locally if you need to know which Organization a token is bound to before calling an MCP endpoint.Scopes
| Scope | What It Grants |
|---|
richtext.read | Read documents, Charms, chats, collections, assets |
richtext.write | Create, update, delete documents and related resources |
If a client omits the scope parameter, every scope that client is allowed to have is granted by default. In practice, scope-down by request when you only need read access.Token Lifetimes
| Token | Lifetime | Notes |
|---|
| Authorization code | 10 minutes | Single-use |
| Access token | 1 hour | Refresh before expiry |
| Refresh token | 30 days | Rotated on every refresh — the new refresh token replaces the old one |
| Dynamic client | Cleaned up after 90 days of inactivity | Re-register if your client goes idle for long stretches |
Refresh-token rotation is real: a refresh call returns a new refresh token, and the old one stops working. If your application loses a refresh token (network blip, crashed write), the user has to go through consent again.Server-to-Server Clients
For a backend integration that runs without a user in the loop, an Organization Owner can create a scoped OAuth client with a client secret directly in CharmIQ. These confidential clients:- Have a
client_id and client_secret you store on your server. - Are bound to one Organization (the one that created them).
- Authenticate at
/oauth/token using client_credentials-style flows or pre-authorized refresh tokens, depending on configuration. - Honor the same scopes as user-facing clients.
This is the right path when you don't want a human consent step every time. The setup lives in CharmIQ's authorization settings — Managing Integrations has the admin walkthrough.Calling an MCP Endpoint
Once you have a token, every MCP call is the same: an HTTP POST carrying a JSON-RPC envelope.curl -X POST https://api.charmiq.ai/mcp/vfs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_dir",
"arguments": { "uri": "root" }
}
}'
A standard MCP SDK does all of this for you — you call client.callTool({ name, arguments }) and the transport layer handles JSON-RPC framing. The flow above is what to expect if you're writing the transport yourself.Revocation and Visibility
Users can revoke any inbound connection from CharmIQ's authorization settings. Organization Owners can also revoke a client across the entire Organization in one action. A revoked client's tokens stop working immediately — the next API call returns a 401.CharmIQ surfaces every active inbound connection in the user's settings: the client's name, when it was authorized, the granted scopes, and the last time it was used. Build your application to fail gracefully on the next 401 and re-enter the OAuth flow.Patterns
- Discover, don't hard-code. Fetch authorization server metadata at startup. URLs may move; the discovery document won't.
- Scope down. If your application only reads, request
richtext.read. The consent screen is shorter, and the user is more likely to approve. - Refresh proactively. Refresh well before the 1-hour expiry so an in-flight request never hits 401. Tokens are cheap; retries aren't.
- One token per Organization, always. Never reuse a token across Organizations — it won't work, and trying to looks like an attack to the audit log.
- Treat refresh tokens like secrets. They're 30-day credentials that can pull a fresh access token whenever they like. Store them server-side, never in client JavaScript.
Next Steps
→ Build on CharmIQ — MCP — The surface map: what each server does and where to look next.→ Connecting External Tools to CharmIQ — End-user setup for the off-the-shelf clients.→ OAuth in Applications — The same OAuth infrastructure, used the other direction: a CharmIQ Application authenticating outward.