Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developers.notion.com/llms.txt

Use this file to discover all available pages before exploring further.

Use OAuth when the API you’re connecting to requires user authorization, such as GitHub, Google, Salesforce, and most SaaS APIs. You register an OAuth capability on your worker, deploy, complete the authorization flow via the CLI, and then call accessToken() in your code to get a valid token.

Define an OAuth capability

Call worker.oauth() with your provider’s OAuth 2.0 endpoints and credentials:
src/index.ts
import { Worker } from "@notionhq/workers";

const worker = new Worker();
export default worker;

const githubAuth = worker.oauth("githubAuth", {
  name: "github-oauth",
  authorizationEndpoint: "https://github.com/login/oauth/authorize",
  tokenEndpoint: "https://github.com/login/oauth/access_token",
  scope: "repo user",
  clientId: process.env.GITHUB_CLIENT_ID ?? "",
  clientSecret: process.env.GITHUB_CLIENT_SECRET ?? "",
});
Store your clientId and clientSecret as secrets, not in code:
ntn workers env set GITHUB_CLIENT_ID=xxx GITHUB_CLIENT_SECRET=yyy

Configuration options

PropertyRequiredDescription
nameYesUnique identifier for this OAuth connection
authorizationEndpointYesThe provider’s OAuth 2.0 authorization URL
tokenEndpointYesThe provider’s OAuth 2.0 token exchange URL
clientIdYesYour OAuth app’s client ID
clientSecretYesYour OAuth app’s client secret
scopeYesSpace-separated list of OAuth scopes to request
authorizationParamsNoAdditional parameters to include in the authorization request
accessTokenExpireMsNoDefault token expiry in milliseconds (for providers that don’t return expires_in)

Deploy and authorize

Setting up OAuth requires multiple steps in a specific order. The worker must be deployed before you can store secrets or start the OAuth flow:
1

Deploy your worker

If you haven’t deployed at least once already, do so first. The first deploy registers the worker with Notion. Your OAuth credentials won’t be available yet (that’s expected).
ntn workers deploy
2

Get your redirect URL

ntn workers oauth show-redirect-url
You’ll need this when creating the OAuth app with your provider.
3

Create an OAuth app with your provider

Go to your provider’s developer settings (e.g., GitHub Developer Settings, Google Cloud Console) and create an OAuth app. Add the redirect URL from the previous step as an authorized redirect URI. Copy the client ID and client secret.
4

Store your OAuth credentials

ntn workers env set GITHUB_CLIENT_ID=xxx GITHUB_CLIENT_SECRET=yyy
5

Deploy again

Redeploy so the worker picks up the credentials:
ntn workers deploy
6

Start the OAuth flow

ntn workers oauth start githubAuth
This opens a browser window where you authorize the connection. Once complete, the worker runtime stores the token securely.

Use the token

Call accessToken() on the OAuth capability object to get a valid token. The runtime handles refresh automatically:
import { j } from "@notionhq/workers/schema-builder";

worker.tool("getGitHubRepos", {
  title: "Get GitHub repos",
  description: "Fetch the user's GitHub repositories",
  schema: j.object({}),
  execute: async () => {
    const token = await githubAuth.accessToken();
    const response = await fetch("https://api.github.com/user/repos", {
      headers: { Authorization: `Bearer ${token}` },
    });
    return response.json();
  },
});
This works the same way in syncs:
import * as Builder from "@notionhq/workers/builder";

worker.sync("githubIssuesSync", {
  database: issues,
  execute: async (state) => {
    const token = await githubAuth.accessToken();
    const response = await fetch("https://api.github.com/issues", {
      headers: { Authorization: `Bearer ${token}` },
    });
    const items = await response.json();
    return {
      changes: items.map((issue) => ({
        type: "upsert" as const,
        key: String(issue.id),
        properties: {
          Title: Builder.title(issue.title),
          "Issue ID": Builder.richText(String(issue.id)),
        },
      })),
      hasMore: false,
    };
  },
});

Test locally

Once you’ve completed the OAuth flow (via ntn workers oauth start), pull your environment to get a fresh access token locally:
ntn workers env pull
This writes a .env file with all your worker’s secrets, including a fresh OAuth access token. The server refreshes the token automatically before returning it, so the token you get is always valid. You can then test your capability locally with the --local flag:
ntn workers exec getGitHubRepos --local
OAuth tokens expire. If you get a 401 from the provider, run ntn workers env pull again to get a refreshed token.

Examples

Google

const googleAuth = worker.oauth("googleAuth", {
  name: "google-oauth",
  authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
  tokenEndpoint: "https://oauth2.googleapis.com/token",
  scope: "https://www.googleapis.com/auth/calendar.readonly",
  clientId: process.env.GOOGLE_CLIENT_ID ?? "",
  clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
  authorizationParams: {
    access_type: "offline",
    prompt: "consent",
  },
});

Salesforce

const salesforceAuth = worker.oauth("salesforceAuth", {
  name: "salesforce-oauth",
  authorizationEndpoint: "https://login.salesforce.com/services/oauth2/authorize",
  tokenEndpoint: "https://login.salesforce.com/services/oauth2/token",
  scope: "api refresh_token",
  clientId: process.env.SALESFORCE_CLIENT_ID ?? "",
  clientSecret: process.env.SALESFORCE_CLIENT_SECRET ?? "",
});
Some providers (like Salesforce) don’t return expires_in in their token response. Set accessTokenExpireMs to a sensible default (e.g., 3600000 for 1 hour) so the runtime knows when to refresh.

Next steps

Secrets

Store API keys and OAuth credentials securely.

Syncs

Sync external data into Notion databases.