Developers

OAuth 2.1 for the SpeakNotes API

Authorization code with PKCE, refresh token rotation, dynamic client registration, and revocation. Use it when your app acts on behalf of other SpeakNotes users.

Discovery

Both metadata documents are public. MCP clients find them automatically from the 401 challenge the API returns.

# Authorization server metadata (RFC 8414)
curl https://speaknotes.io/.well-known/oauth-authorization-server

# Protected resource metadata (RFC 9728)
curl https://api.speaknotes.io/.well-known/oauth-protected-resource

The flow

  1. 1

    Register a client

    POST to the registration endpoint with your redirect URIs. Public clients use PKCE and get no secret; confidential clients get one. https, loopback http, and custom app schemes are all accepted.

    curl -X POST https://api.speaknotes.io/oauth/register \
      -H "Content-Type: application/json" \
      -d '{
        "client_name": "Your app",
        "redirect_uris": ["https://your.app/callback"],
        "token_endpoint_auth_method": "none",
        "scope": "notes:read notes:write summaries:write"
      }'
  2. 2

    Send the user to authorize

    Open the authorization endpoint with your client id, redirect URI, scopes, state, and an S256 code challenge. The user signs in, sees exactly which scopes you asked for, and approves or declines.

    https://speaknotes.io/oauth/authorize
      ?client_id=sn_client_...
      &redirect_uri=https://your.app/callback
      &response_type=code
      &scope=notes:read%20summaries:write
      &state=RANDOM
      &code_challenge=BASE64URL_SHA256_OF_VERIFIER
      &code_challenge_method=S256
  3. 3

    Exchange the code

    POST the code and your code verifier to the token endpoint. Codes are single use and expire in a minute; replaying one revokes the tokens it produced.

    curl -X POST https://api.speaknotes.io/oauth/token \
      -H "Content-Type: application/json" \
      -d '{
        "grant_type": "authorization_code",
        "client_id": "sn_client_...",
        "code": "...",
        "redirect_uri": "https://your.app/callback",
        "code_verifier": "..."
      }'
  4. 4

    Refresh

    Access tokens last an hour. Refresh tokens rotate on every use, and presenting a revoked one drops the whole grant, which is what stops a stolen token being useful.

    curl -X POST https://api.speaknotes.io/oauth/token \
      -H "Content-Type: application/json" \
      -d '{
        "grant_type": "refresh_token",
        "client_id": "sn_client_...",
        "refresh_token": "sn_rt_..."
      }'

access_token: 1h · refresh_token: 60d, rotating · code: 60s, single use

Requesting scopes

Ask for the least you need. A refresh may narrow scope but never widen it, and users can see and revoke any app from their settings.

Revocation

POST either token to the revocation endpoint. Revoking one half kills the pair. Revoking a token that never existed is still a success, per RFC 7009.

curl -X POST https://api.speaknotes.io/oauth/revoke \
  -H "Content-Type: application/json" \
  -d '{"client_id": "sn_client_...", "token": "sn_rt_..."}'

MCP clients

An MCP client needs none of this by hand. It calls a tool, gets a 401 pointing at the resource metadata, registers itself, and walks the user through the same consent screen.