Skip to main content
AI/MLjeremylongshore

notion-core-workflow-a

'Query, filter, and manage Notion databases and pages.

Stars
2,267
Source
jeremylongshore/claude-code-plugins-plus-skills
Updated
2026-05-31
Slug
jeremylongshore--claude-code-plugins-plus-skills--notion-core-workflow-a
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/jeremylongshore/claude-code-plugins-plus-skills/HEAD/plugins/saas-packs/notion-pack/skills/notion-core-workflow-a/SKILL.md -o .claude/skills/notion-core-workflow-a.md

Drops the SKILL.md into .claude/skills/notion-core-workflow-a.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

Notion Core Workflow A — Databases & Pages

Overview

Primary workflow for Notion integrations: querying databases with filters/sorts, creating pages with typed properties, updating page properties, and retrieving page content.

Prerequisites

  • Completed notion-install-auth setup
  • A Notion database shared with your integration
  • Understanding of your database's property schema

Authentication

Every call below uses a Client authenticated with an integration token (process.env.NOTION_TOKEN). Token creation, secret storage, and sharing a database with the integration are covered end-to-end in the notion-install-auth skill — complete it first. Never hardcode the token; read it from the environment.

Instructions

The workflow is six steps. Steps 1–2 (schema + filtered query) are the skeleton you almost always start with, shown here in full. Steps 3–6 (filter syntax by type, page creation, updates/archive, pagination) live in the full walkthrough so this file stays scannable.

Step 1: Retrieve Database Schema

Always inspect the schema first — property names and types drive every filter and write. databases.retrieve returns db.properties keyed by property name.

import { Client } from '@notionhq/client';

const notion = new Client({ auth: process.env.NOTION_TOKEN });

async function getDatabaseSchema(databaseId: string) {
  const db = await notion.databases.retrieve({ database_id: databaseId });
  for (const [name, prop] of Object.entries(db.properties)) {
    console.log(`${name}: ${prop.type}`);
    if (prop.type === 'select') {
      console.log('  Options:', prop.select.options.map(o => o.name));
    }
  }
  return db.properties;
}

Step 2: Query with Filters

Notion filters use a nested structure keyed by property type, and combine with and / or. sorts and page_size (max 100) tune the result set.

async function queryWithFilters(databaseId: string) {
  const response = await notion.databases.query({
    database_id: databaseId,
    filter: {
      and: [
        { property: 'Status', select: { equals: 'In Progress' } },
        { property: 'Priority', select: { does_not_equal: 'Low' } },
      ],
    },
    sorts: [{ property: 'Priority', direction: 'ascending' }],
    page_size: 50,
  });
  return response.results;
}

Steps 3–6: Filter syntax, create, update, paginate

See the full walkthrough for copy-paste code:

  • Step 3 — Filter syntax by property type. Every property type (text, number, select, date, checkbox, people, relation, formula, rollup, timestamp) has its own filter shape.
  • Step 4 — Create a page with all property types. One pages.create call showing the correct payload for each typed property.
  • Step 5 — Update & archive. pages.update to change properties, or set archived: true to soft-delete.
  • Step 6 — Paginate all results. Loop on has_more / next_cursor to pull a full database beyond the 100-row page limit.

Output

  • Database schema retrieved with property types and options
  • Filtered and sorted query results
  • Pages created with typed properties
  • Pages updated and archived

Error Handling

Error Cause Solution
validation_error Property name mismatch or wrong type Use databases.retrieve to check schema
object_not_found Database not shared with integration Add integration via Connections
rate_limited (429) >3 requests/second average Respect Retry-After header
Empty results Filter too restrictive or no data Test with no filter first

Examples

Reading queried pages back into plain values requires switching on each property's type. A reusable getPropertyValue helper plus a full "flatten a database into an array of objects" example live in examples & helpers:

// Excerpt — full helper in references/examples.md
function getPropertyValue(property: any) {
  switch (property.type) {
    case 'title':  return property.title.map((t: any) => t.plain_text).join('');
    case 'number': return property.number;
    case 'select': return property.select?.name ?? null;
    // ...rich_text, multi_select, date, checkbox, url, email, formula
    default:       return null;
  }
}

Resources