Skip to main content
An OAuth app lets your users connect a tool to Affinity without sharing an API key. You create the app in your own organization, and only your users can authorize it. Affinity calls this a second-party app. Use an OAuth app when a tool acts on behalf of a person and each person should sign in as themselves. If a script acts as a single service account, an API key is simpler. MCP clients that publish a Client ID Metadata Document (CIMD) don’t need an app. You add them by their URL instead. See MCP clients with a metadata URL (CIMD).

Prerequisites

  • Plan: Scale, Advanced, or Enterprise.
  • Access: OAuth apps are in early access. If you don’t see OAuth App under New App in Manage Apps, ask your Affinity contact to turn it on for your organization.
  • Permission: Manage all OAuth apps. By default, only Administrators have it.
  • IP allowlist: to set one, you may also need the Manage IP allowlist permission.

Create the app

  1. In Affinity, go to Settings → Manage Apps.
  2. Click New App, then OAuth App. If you can’t create API keys, the button is called New OAuth App instead.
  3. Enter a Name and a Description. Your users see the name on the consent screen.
  4. Choose the Client type (the default is Public):
    • Public is for apps whose code runs on the user’s device, such as mobile, desktop or single-page apps, or anywhere else the code ships to the user. A public client has no secret and must use PKCE.
    • Confidential is for a server you control, where a secret can stay private. You get a client secret, shown once.
  5. Enter one Redirect URI per line, as a full URL such as https://example.com/callback.
  6. Choose the Allowed scopes, which decide what the app can do for your users. Pick at least one.
  7. Optionally, set an API version, an IP allowlist, an icon and a website URL.
  8. Click Create.
The Manage Apps settings page with the New App button

Manage Apps lists your API keys and your OAuth apps in one table.

The New OAuth App form showing the name, description and client type fields

The app creation form: name and client type.

The New OAuth App form showing redirect URIs, allowed scopes and the IP allowlist

Redirect URIs, allowed scopes and the IP allowlist, further down the same form.

After you click Create, Affinity opens the app’s detail page, where you’ll find the Client ID. For a confidential app, this happens after you close the client secret dialog. A client ID looks like 1f0c2b7a-3d4e-4a1b-9c8d-5e6f70819aa2.
A confidential app shows its client secret only once. Copy it and store it like a password. Affinity keeps only a hash, so you can’t view it again or rotate it. If you lose it, you’ll need to delete the app and create a new one, which disconnects every user.
The Client Secret Created dialog with a copy button

Affinity shows the client secret once.

MCP clients with a metadata URL (CIMD)

Some MCP clients don’t need you to create an app. Instead, their client_id is a URL that points to a JSON file about the client, called a Client ID Metadata Document (CIMD). For example, Claude uses https://claude.ai/oauth/mcp-oauth-client-metadata.

Built-in MCP clients

Clients such as Claude, ChatGPT and Notion are already set up. You can turn each one on or off in Settings → Affinity MCP. For setup steps, see Supported clients.

Add an MCP client by its Client URL (CIMD)

To use another MCP client with a metadata URL, add it in Manage Apps.
Adding a client by its Client URL (CIMD) is in early access. If you don’t see Client URL (CIMD) under New App in Manage Apps, ask your Affinity contact to turn it on.
  1. Go to Settings → Manage Apps, click New App, then Client URL (CIMD). If you can’t create API keys, click Add by Client URL (CIMD) instead.
  2. Paste the client’s metadata URL, such as https://acme.example.com/oauth/client.json, and click Preview.
  3. Review the preview. Check the hostname: the name and logo come from the vendor, so anyone can set them. You’ll also see a warning if every redirect URI is on localhost.
  4. Optionally, narrow the Allowed Scopes or add an IP Allowlist.
  5. Click Add App. Your users can now sign in with the client.
Nothing is saved until you click Add App. If Affinity can’t read the URL, the form tells you why. Affinity saves a copy of the document when you add the client. If the vendor updates it, open the client and click Refresh Metadata. To change the allowed scopes or the IP allowlist, click Edit Details. The name, website and redirect URIs always come from the document. To remove the client, open it and click Revoke App. This disconnects everyone in your organization who uses it. To bring it back, add it again with the same URL. Your users will need to connect again.

CIMD metadata document requirements

If you build an MCP client, your metadata document must:
  • Be served over https at the exact URL you use as client_id, with no redirects, and respond within 5 seconds.
  • Set client_id to that same URL.
  • Include client_name and at least one redirect_uris entry.
  • Use redirect URIs on the same registrable domain as the client_id, or on localhost. For example, a client_id on app.acme.example.com can redirect to https://auth.acme.example.com/callback or http://127.0.0.1/callback.
  • Include code in response_types, if you set it.
  • Set token_endpoint_auth_method to none or private_key_jwt. Either way, Affinity treats the client as public: it uses PKCE and has no client secret.

Scopes

api doesn’t grant mcp, so ask for mcp if your app talks to the MCP server. If you leave out the scope parameter when authorizing, Affinity grants every scope the app is approved for. Ask only for the scopes you need.

Authorize a user

Send the user to the authorize endpoint. A public client must include the PKCE parameters, and a confidential client can leave them out.
The user signs in, reviews the scopes and clicks Allow Access. Affinity then redirects to your redirect URI with code and state. Users can uncheck scopes before allowing, so check the scope field in the token response instead of assuming you got everything you asked for.
The Affinity OAuth consent screen listing the requested scopes

The consent screen. Your users can uncheck a scope before allowing access.

  • response_type must be code, since it’s the only supported flow.
  • redirect_uri must exactly match a registered URI. Only localhost, 127.0.0.1 and [::1] can use a different port.
  • PKCE supports S256 only, not plain.
  • A public app must send PKCE. A confidential app can send it too, but then it must also send the verifier in the next step.
  • The code expires after 5 minutes and can only be used once.

Exchange the code for tokens

A public client sends client_id and code_verifier in the request body. A confidential client uses HTTP Basic authentication, with the client ID as the username and the client secret as the password.
A confidential client can also send client_id and client_secret as form fields, but if the Basic header is present, Affinity ignores them. If the client sent a PKCE challenge when authorizing, it must also send code_verifier here. The response looks like this:
Access tokens last 1 day and refresh tokens last 90 days.

Refresh a token

A public client gets a new refresh token each time and the old one stops working, so always store the new one. A confidential client can keep using the same refresh token until it expires, even though the response includes a new one.

Call the API

Read-only endpoints need api.read, and endpoints that change data need api. Some read-only endpoints use POST, such as POST /v2/notes/search.

API version

For each request, Affinity picks the API version in this order:
  1. The X-Affinity-Api-Version header.
  2. The app’s pinned version.
  3. 2024-01-01, the oldest published version.
New apps are pinned to the current version when you create them. To be sure which version you get, send the header. An unsupported version returns 400 with a validation error on the header. See Versioning for the published versions.

Discovery

Affinity publishes its OAuth metadata at https://login.affinity.co/.well-known/oauth-authorization-server, and its signing keys at https://login.affinity.co/.well-known/jwks.json. Clients that read RFC 8414 metadata don’t need any endpoint configuration.

Connect the app to the Affinity MCP server

Your OAuth app can also call the hosted Affinity MCP server at https://mcp.affinity.co/mcp.
  1. Give the app the mcp scope, or mcp.read for read-only access. Add offline_access if the client runs in the background.
  2. Point your MCP client at https://mcp.affinity.co/mcp.
  3. The client reads https://mcp.affinity.co/.well-known/oauth-protected-resource/mcp, finds https://login.affinity.co and runs the flow above.
The MCP server passes your token to the Affinity API unchanged, so the same scope rules and rate limits apply as for a direct API call.

Restrict the app to known IP addresses

With an IP allowlist, the app accepts calls only from the addresses you list.
  • Add up to 100 entries, each an IPv4 address, IPv6 address or CIDR range.
  • Leave the list empty to allow any address.
  • Calls from other addresses return 403 with Access denied.
  • The allowlist applies only to Affinity API v2, not to the older v1 API.

Rate limits

OAuth apps share the same per-user, per-organization and concurrent limits as API keys, and get the same rate limit headers. When you go over a limit, the API returns 429. See Rate limits for the current values.

Errors

Manage the app

Go to Settings → Manage Apps and click the app. The detail page shows the client ID, redirect URIs, scopes, allowed IPs, authentication method, creator and pinned version.
The OAuth app detail page

The app detail page: client ID, redirect URIs, scopes, allowed IPs and the pinned API version.

From there, you can edit the name, description, redirect URIs, scopes, IP allowlist and version.
Revoking an app you created deletes it, and you can’t undo it. The app loses API access, every user in your organization who connected it is disconnected, and its client ID stops working, so it can’t be authorized again. To restore access, create a new app and ask your users to authorize it.
The revoke confirmation dialog

Revoking disconnects every user.

Current limits

  • There’s no token revocation endpoint. A token stops working when it expires, the user disconnects the app, or the app is revoked. Without offline_access, it also stops when the user signs out of Affinity.