Authentication
The External API accepts two credential types, both sent as a Bearer token:
| Credential | Prefix | For |
|---|---|---|
| Personal Access Token (PAT) | csm_pat_ | Scripts and integrations acting as you |
| OAuth access token | csm_oat_ | Third-party apps acting on behalf of a user |
Sessions created in the Consortium app use a separate, device-keypair-based scheme — that surface is not part of the External API.
Personal Access Tokens
Section titled “Personal Access Tokens”Create a PAT in the app under Settings → Developer → External API, or programmatically with a signed-in device token:
POST /v1/account/external-tokensAuthorization: Bearer <device token>- Expiry is optional: 1–3650 days, or non-expiring if unset.
- Scopes are least-privilege — grant only what the caller needs.
- Machine-scoped credentials (daemons, VPS hosts) cannot mint PATs.
Validate your wiring by calling /me:
curl https://api.consortium.dev/external/v1/me \ -H "Authorization: Bearer $CONSORTIUM_PAT"import { ConsortiumClient } from '@consortium/sdk';
const client = ConsortiumClient.fromPat({ token: process.env.CONSORTIUM_PAT!, userAgent: 'my-integration/1.0',});
const me = await client.me();console.log(me.account.id, me.token.scopes);console.log(me.meta.rateLimit); // { limit, remaining, reset }import osfrom consortium import ConsortiumClient
client = ConsortiumClient.from_pat(os.environ["CONSORTIUM_PAT"])
me = client.me()print(me.data.account.username, me.data.token.scopes)print("rate limit remaining:", me.meta.rate_limit.remaining)/me returns the token’s scopes, the account, plan limits, and any active
deprecation notices — call it first to validate wiring.
Scopes
Section titled “Scopes”| Scope | Grants | Notes |
|---|---|---|
sessions:read | List sessions and message metadata | |
sessions:write | Send messages into active sessions | Relay, blocks up to 30 s |
sessions:content | Read message content | Requires a key grant |
machines:read | List machines and their state | |
machines:invoke | Run commands on a machine | PAT-only — never grantable to OAuth apps |
machines:invoke:verified | — | Reserved; no endpoint yet |
artifacts:read | List artifacts | |
artifacts:content | Read artifact content | Requires a key grant |
artifacts:write | — | Reserved; no endpoint yet |
webhooks:manage | Create and revoke webhook subscriptions | |
usage:read | Read API usage counts | |
profile:read | Read identity for the principal |
OAuth 2.0 (third-party apps)
Section titled “OAuth 2.0 (third-party apps)”Register an app under Settings → Developer (or
POST /v1/developer/apps). You get a csm_oapp_… client ID and a client
secret (shown once, rotatable).
The only supported flow is authorization code with PKCE (S256) —
plain challenges, implicit, and client-credentials grants are rejected.
- Send the user to
GET /external/v1/oauth/authorizewithclient_id,redirect_uri,scope,state,code_challenge. - The user approves on the Consortium consent screen, which lists the requested scopes.
- Exchange the code at
POST /external/v1/oauth/token(client authentication via HTTP Basic or body parameters).
Token response:
{ "access_token": "csm_oat_...", "refresh_token": "csm_ort_...", "token_type": "Bearer", "expires_in": 3600, "scope": "sessions:read profile:read"}- Access tokens live 1 hour; refresh tokens 30 days.
POST /external/v1/oauth/revoke(RFC 7009) andPOST /external/v1/oauth/introspect(RFC 7662) are available with client credentials.- OAuth apps can request most scopes, but
machines:invokeandmachines:invoke:verifiedare PAT-only by design.
Failure modes
Section titled “Failure modes”Missing or bad credentials return 401 missing_token / 401 invalid_token;
a valid token without the needed scope returns 403 insufficient_scope
with a required array. See Errors for the full catalogue.