Skip to main content

Get credentials

1

Create an integration

Go to Notion Integrations and click New integration. Set the type to Public, this is required for OAuth with external users.
2

Configure OAuth settings

In the integration settings, scroll to OAuth Domain & URIs. Add your redirect URI:
https://auth.example.com/auth/oauth/notion/callback
Set Redirect URIs and save.
3

Copy credentials

Under Basic Information, copy the OAuth client ID and generate an OAuth client secret.

Configuration

lib/kavach.ts
import { createKavach } from 'kavachos';
import { oauth } from 'kavachos/auth';

const kavach = await createKavach({
  database: { provider: 'postgres', url: process.env.DATABASE_URL! },
  secret: process.env.KAVACH_SECRET!,
  baseUrl: 'https://auth.example.com',
  plugins: [
    oauth({
      providers: [
        {
          id: 'notion', 
          clientId: process.env.NOTION_CLIENT_ID!, 
          clientSecret: process.env.NOTION_CLIENT_SECRET!, 
        },
      ],
    }),
  ],
});
NOTION_CLIENT_ID=your-notion-oauth-client-id
NOTION_CLIENT_SECRET=secret_...

Endpoints

EndpointURL
Authorizationhttps://api.notion.com/v1/oauth/authorize
Tokenhttps://api.notion.com/v1/oauth/token
User infoEmbedded in token response (owner.user)

Scopes

Notion does not use granular OAuth scopes. Permissions are configured at the integration level in the Notion UI. When a user authorizes your integration, they choose which pages and databases to share.

User data returned

FieldSourceNotes
idowner.user.idStable Notion user UUID
emailowner.user.person.emailPresent only for person-type authorizations
nameowner.user.nameFull display name
avatarowner.user.avatar_urlMay be null if the user has no profile photo
Notion returns user identity as part of the token exchange response, there is no separate /me endpoint. KavachOS caches the token payload internally so no extra network call is made.
The email field is only present when a person authorizes the integration. If a workspace bot is the owner (owner.type === "workspace"), the email will be absent. Always check for userInfo.email before using it.

Workspace data

The token response also includes workspace context, available in userInfo.raw:
const { userInfo } = await oauth.handleCallback('notion', code, state, redirectUri);

// Access workspace info from the raw token payload
const raw = userInfo.raw as { workspace_id: string; workspace_name: string };
console.log(raw.workspace_id);   // e.g. "1234abcd-..."
console.log(raw.workspace_name); // e.g. "Acme Corp"
This is useful for multi-workspace SaaS apps where you want to scope data per Notion workspace.
Last modified on April 18, 2026