Skip to content

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-server
json
{
  "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-resource
json
{
  "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_token grant type and the offline_access scope 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=S256

The user signs in to Pragma and approves the connection on a consent page. Two things happen server-side that you should know about:

  • offline_access is 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 resource or audience parameter 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.