Portal Community

When to Use

Configuration

Authentication

FieldRequiredDescription
AuthenticationMethodRequired"ApiToken" or "OAuth2".
ApiTokenRequiredNotion Internal Integration Token starting with secret_.
ClientIdOAuth2OAuth2 client ID.
ClientSecretOAuth2OAuth2 client secret.
AccessTokenOAuth2OAuth2 access token.

Operation Fields

FieldRequiredDescription
DatabaseRequiredTarget Notion database UUID.
FilterOptionalNotion filter object. Omit to return all pages. See filter syntax below.
SortsOptionalArray of Notion sort objects. Example: [{"property":"Due","direction":"ascending"}].
ReturnAllOptionalWhen true, automatically pages through all results. Default: false.
LimitOptionalMax results per call (1–100). Used when ReturnAll is false. Default: 100.
StartCursorOptionalPagination cursor from a previous call's next_cursor.

Sample Configuration

{
  "resource": "database",
  "operation": "query",
  "AuthenticationMethod": "ApiToken",
  "ApiToken": "{{ $credentials.notionApiToken }}",
  "Database": "{{ $vars.sprintDatabaseId }}",
  "Filter": {
    "property": "Status",
    "select": { "equals": "In Progress" }
  },
  "Sorts": [
    { "property": "Priority", "direction": "descending" }
  ],
  "ReturnAll": false,
  "Limit": 50
}
Compound filters: Combine multiple conditions using and or or at the top level. Example: {"and":[{"property":"Status","select":{"equals":"In Progress"}},{"property":"Assignee","people":{"contains":"user-uuid"}}]}

Validation Errors

Error CodeCause
NOTION_VALIDATION_FAILEDDatabase field is empty or not a valid UUID.
NOTION_INVALID_REQUESTFilter or sort object is malformed — check property names match the database schema exactly (case-sensitive).
NOTION_CONFLICTFilter references a property type that does not exist in the database (e.g., filtering as select on a text property).
NOTION_PERMISSION_DENIEDIntegration not shared with this database.
NOTION_NOT_FOUNDDatabase UUID does not exist.
NOTION_RATE_LIMITEDRate limit exceeded. ReturnAll on large databases can trigger this — use Limit + cursor loop with a Delay node.

Output

Success Port

FieldTypeDescription
resultsarrayArray of page objects. Each page contains id, url, created_time, last_edited_time, archived, parent, and a properties object with all property values.
has_morebooleanWhether additional results exist past the current page.
next_cursorstringCursor for next call when has_more is true. Null otherwise.
objectstringAlways "list".

Error Port

On failure the error port receives errorCode, message, and httpStatus. Handle NOTION_INVALID_REQUEST by logging the filter and database ID for debugging.

Sample Output

{
  "object": "list",
  "results": [
    {
      "id": "e5f6a7b8-c9d0-1234-efab-345678901234",
      "url": "https://www.notion.so/e5f6a7b8c9d01234efab345678901234",
      "created_time": "2024-06-01T08:00:00.000Z",
      "last_edited_time": "2024-06-10T14:00:00.000Z",
      "archived": false,
      "properties": {
        "Name": {
          "type": "title",
          "title": [{ "plain_text": "Build login page" }]
        },
        "Status": {
          "type": "select",
          "select": { "name": "In Progress" }
        },
        "Assignee": {
          "type": "people",
          "people": [{ "name": "Sarah Chen", "id": "user-uuid-1" }]
        },
        "Due": {
          "type": "date",
          "date": { "start": "2024-06-15" }
        }
      }
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Expression Reference

ExpressionTypeUse
{{ $output.queryDatabase.results.length }}numberCount matching pages
{{ $output.queryDatabase.results[0].id }}stringFirst page UUID — pass to page/update
{{ $output.queryDatabase.results[0].properties.Name.title[0].plain_text }}stringExtract page title text
{{ $output.queryDatabase.results[0].properties.Status.select.name }}stringRead select property value
{{ $output.queryDatabase.results[0].properties.Due.date.start }}stringRead date property value
{{ $output.queryDatabase.next_cursor }}stringPass to next call for manual pagination

Node Policies & GuardRails

PolicyDetail
Property names are case-sensitiveFilter and sort property names must exactly match the database schema (e.g., "Status" not "status"). A mismatch causes NOTION_INVALID_REQUEST. Verify names with database/get first.
Pagination for large databasesDatabases with thousands of rows should use cursor-based manual pagination instead of ReturnAll to avoid rate limiting. Store the cursor in a workflow variable between runs.
Filter by last_edited_time for syncFor incremental sync patterns, filter by last_edited_time using a timestamp filter: {"timestamp":"last_edited_time","last_edited_time":{"after":"2024-06-01T00:00:00Z"}}.
Archived pages excluded by defaultThe query API excludes archived pages by default. To include them, add a specific filter: {"property":"...","archived":true} — however this is rarely needed in practice.
Credentials in Credentials ManagerAlways use {{ $credentials.notionApiToken }} — never hard-code the token.

Workflow Examples

Example 1 — Sprint Status Report

Query all "In Progress" tasks, loop through each, extract key fields, and post a formatted summary to Slack.

// Step 1: database/query
{
  "Database": "{{ $vars.sprintDbId }}",
  "Filter": { "property": "Status", "select": { "equals": "In Progress" } },
  "Sorts": [{ "property": "Priority", "direction": "descending" }],
  "ReturnAll": true
}
// Step 2: Loop over results
// Step 3 per item: build message line
const title = item.properties.Name.title[0].plain_text;
const assignee = item.properties.Assignee.people[0]?.name ?? "Unassigned";
const due = item.properties.Due.date?.start ?? "No date";
return `- ${title} (${assignee}) — Due: ${due}`;
// Step 4: Slack/postMessage with joined lines

Example 2 — Daily Updated Records Digest

Every morning, find all pages edited in the past 24 hours and email a digest to the team lead.

// Step 1: ScheduledTrigger (daily 8am)
// Step 2: Function node — compute yesterday ISO string
const yesterday = new Date(Date.now() - 86400000).toISOString();
return { yesterday };

// Step 3: database/query
{
  "Database": "{{ $vars.crmDbId }}",
  "Filter": {
    "timestamp": "last_edited_time",
    "last_edited_time": { "after": "{{ $json.yesterday }}" }
  },
  "ReturnAll": true
}
// Step 4: EmailSmtp with results count and list

Example 3 — Paginated Export to External Database

Export all rows from a large Notion database into an external SQL database using cursor-based pagination.

// Step 1: database/query (Limit: 100, StartCursor: {{ $vars.cursor ?? null }})
// Step 2: Loop over results → insert each row into MongoDB/Postgres
// Step 3: IfCondition — has_more === true
//   True: VariableAssignment cursor = next_cursor → loop back to Step 1
//   False: StopWorkflow