Skip to content

SOP 002: Authentication Setup

Fresh Last Updated: January 2025
FieldValue
SOP IDSOP-002
Version1.0
StatusActive
DifficultyBeginner

Purpose

Configure secure authentication for your Notion API integration, including environment setup and the JavaScript SDK.

Prerequisites

Authentication Flow

Step-by-Step Instructions

Step 1: Install Dependencies

bash
# Create a new project
mkdir my-notion-app
cd my-notion-app
npm init -y

# Install required packages
npm install @notionhq/client dotenv

Step 2: Create Environment File

Create .env in your project root:

env
# Notion API Configuration
NOTION_API_KEY=secret_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# Optional: Default page/database IDs
NOTION_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
NOTION_PAGE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Security Warning

Add .env to your .gitignore immediately:

bash
echo ".env" >> .gitignore

Step 3: Initialize the Notion Client

Create notion.js:

javascript
require('dotenv').config();
const { Client } = require('@notionhq/client');

// Validate environment variable
if (!process.env.NOTION_API_KEY) {
  throw new Error('NOTION_API_KEY environment variable is required');
}

// Initialize with authentication
const notion = new Client({
  auth: process.env.NOTION_API_KEY,
  // Optional: customize behavior
  timeoutMs: 30000,  // 30 second timeout
  notionVersion: '2022-06-28'  // API version
});

module.exports = notion;

Step 4: Create a Reusable API Wrapper

For production applications, create a wrapper with error handling:

javascript
// api/notion-client.js
require('dotenv').config();
const { Client, APIErrorCode, isNotionClientError } = require('@notionhq/client');

class NotionAPI {
  constructor() {
    this.client = new Client({
      auth: process.env.NOTION_API_KEY
    });
  }

  async execute(operation) {
    try {
      return await operation();
    } catch (error) {
      if (isNotionClientError(error)) {
        switch (error.code) {
          case APIErrorCode.Unauthorized:
            throw new Error('Invalid API key. Check your NOTION_API_KEY.');
          case APIErrorCode.ObjectNotFound:
            throw new Error('Resource not found. Ensure integration has access.');
          case APIErrorCode.RateLimited:
            // Wait and retry
            const retryAfter = error.headers?.['retry-after'] || 1;
            await this.sleep(retryAfter * 1000);
            return this.execute(operation);
          default:
            throw error;
        }
      }
      throw error;
    }
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  // Convenience methods
  async getPage(pageId) {
    return this.execute(() =>
      this.client.pages.retrieve({ page_id: pageId })
    );
  }

  async queryDatabase(databaseId, filter = {}, sorts = []) {
    return this.execute(() =>
      this.client.databases.query({
        database_id: databaseId,
        filter,
        sorts
      })
    );
  }

  async createPage(databaseId, properties, children = []) {
    return this.execute(() =>
      this.client.pages.create({
        parent: { database_id: databaseId },
        properties,
        children
      })
    );
  }
}

module.exports = new NotionAPI();

Step 5: HTTP Header Authentication (Without SDK)

If you're not using the SDK, include the header manually:

javascript
const response = await fetch('https://api.notion.com/v1/users/me', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${process.env.NOTION_API_KEY}`,
    'Notion-Version': '2022-06-28',
    'Content-Type': 'application/json'
  }
});

const data = await response.json();

Step 6: Verify Authentication

Create test-auth.js:

javascript
const notion = require('./notion');

async function verifyAuth() {
  console.log('Testing Notion API authentication...\n');

  try {
    // Test 1: Get bot user
    const me = await notion.users.me({});
    console.log('✅ Authentication successful');
    console.log(`   Bot: ${me.name}`);
    console.log(`   Type: ${me.type}`);
    console.log(`   ID: ${me.id}\n`);

    // Test 2: List users (requires user read capability)
    try {
      const users = await notion.users.list({});
      console.log(`✅ Can list users (${users.results.length} found)`);
    } catch (e) {
      console.log('⚠️  Cannot list users (capability not enabled)');
    }

    // Test 3: Search (requires content read capability)
    try {
      const search = await notion.search({ query: '', page_size: 1 });
      console.log(`✅ Can search content (${search.results.length} results)`);
    } catch (e) {
      console.log('⚠️  Cannot search (no pages shared with integration)');
    }

    console.log('\n✅ All authentication tests passed!');

  } catch (error) {
    console.error('❌ Authentication failed:', error.message);
    process.exit(1);
  }
}

verifyAuth();

Run the test:

bash
node test-auth.js

Authentication Types

TypeUse CaseToken Management
InternalYour own workspaceSingle API secret
PublicThird-party usersOAuth per user

Verification Checklist

  • [ ] Environment file created with API key
  • [ ] .env added to .gitignore
  • [ ] Notion client initializes without errors
  • [ ] users.me() returns bot information
  • [ ] Error handling implemented for common cases

Troubleshooting

ErrorCodeSolution
Unauthorized401Check API key format starts with secret_
Invalid token401Regenerate token in integration settings
Forbidden403Enable required capability
Not found404Share page with integration

Debug Checklist

See Also

Built with VitePress | Powered by Claude AI