OAuth2 client registration
Pragma's API uses OAuth 2.1 with open dynamic client registration (RFC 7591): any client can register itself programmatically, without a manual app-review step or a pre-shared registration token. This is what lets MCP (Model Context Protocol) clients connect with zero configuration — and you can use the same endpoints for your own integrations.
Discovery
Start from the metadata documents; never hardcode endpoint URLs beyond the base:
sh
curl https://api.pragma.pm/.well-known/oauth-authorization-serverjson
{
"issuer": "https://api.pragma.pm",
"authorization_endpoint": "https://api.pragma.pm/oauth/authorize",
"token_endpoint": "https://api.pragma.pm/oauth/token",
"registration_endpoint": "https://api.pragma.pm/oauth/register",
"revocation_endpoint": "https://api.pragma.pm/oauth/revoke",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"scopes_supported": ["app_content", "offline_access"]
}The protected-resource metadata (RFC 9728) ties the MCP endpoint to this authorization server — MCP clients use it to bootstrap the whole flow:
sh
curl https://api.pragma.pm/.well-known/oauth-protected-resourcejson
{
"resource": "https://api.pragma.pm/mcp",
"authorization_servers": ["https://api.pragma.pm"],
"scopes_supported": ["app_content", "offline_access"]
}Register a client
POST /oauth/register with a standard RFC 7591 body:
sh
curl -X POST https://api.pragma.pm/oauth/register \
-H 'content-type: application/json' \
-d '{
"client_name": "My integration",
"redirect_uris": ["http://127.0.0.1:8765/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"scope": "app_content offline_access"
}'The response contains your client_id (and a client_secret if you chose a confidential auth method). Store it; registration is one-time per client.
Pragma normalizes every registration so the rest of the flow works:
- The
refresh_tokengrant type and theoffline_accessscope are added if missing, so refresh tokens are possible later. - The API audience (
pragma-core-api) is whitelisted on the client — without it the authorization request would be rejected.
You don't need to rely on these fixups: send the full body above and the registration is already correct.
Authorize
Standard authorization-code flow with PKCE (Proof Key for Code Exchange, S256):
https://api.pragma.pm/oauth/authorize
?client_id=<client_id>
&response_type=code
&redirect_uri=http://127.0.0.1:8765/callback
&scope=app_content offline_access
&state=<random>
&code_challenge=<S256 challenge>
&code_challenge_method=S256The user signs in to Pragma and approves the connection on a consent page. Two things happen server-side that you should know about:
offline_accessis surfaced at consent even if you forgot to request it — as an unchecked checkbox. A refresh token is only issued when the user explicitly ticks it. If your integration needs to run unattended, tell your users to enable offline access at the consent step.- The API audience is added automatically — you don't need to send a
resourceoraudienceparameter for tokens to work against the MCP endpoint.
Exchange and refresh tokens
Standard POST /oauth/token with grant_type=authorization_code (plus your PKCE code_verifier), then grant_type=refresh_token to renew. Access tokens are short-lived (on the order of an hour); without a refresh token the user must re-authenticate when the token expires.
Revoke tokens with POST /oauth/revoke.
Use the token
Send it as a Bearer token against the MCP endpoint:
sh
curl https://api.pragma.pm/mcp \
-H 'authorization: Bearer <access_token>' \
...The MCP endpoint is the supported programmatic surface — its 81 tools cover reading and writing the full work graph. See Connect an MCP client.