Portal Community

When to Use

Configuration

Authentication

FieldRequiredDescription
AuthenticationMethodRequired"ApiToken" or "OAuth2".
ApiTokenRequiredNotion Internal Integration Token. Starts with secret_. Store in Credentials Manager.
ClientIdOAuth2OAuth2 client ID — managed via BizFirst Credentials Manager.
ClientSecretOAuth2OAuth2 client secret — managed via BizFirst Credentials Manager.
AccessTokenOAuth2OAuth2 access token — managed via BizFirst Credentials Manager.

Operation Fields

FieldRequiredDescription
DatabaseRequiredNotion database UUID. Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Copy from the database URL in Notion.

Sample Configuration

{
  "resource": "database",
  "operation": "get",
  "AuthenticationMethod": "ApiToken",
  "ApiToken": "{{ $credentials.notionApiToken }}",
  "Database": "{{ $vars.projectDatabaseId }}"
}

Validation Errors

Error CodeCause
NOTION_VALIDATION_FAILEDDatabase field is empty or not a valid UUID format.
NOTION_UNAUTHORIZEDApiToken is invalid or the integration is not connected to the workspace.
NOTION_PERMISSION_DENIEDThe integration has not been shared with this database. Open the database in Notion and add the integration under Connections.
NOTION_NOT_FOUNDNo database exists with the given UUID, or the UUID refers to a page rather than a database.
NOTION_RATE_LIMITED3 requests/second limit exceeded. Add a Delay node before retrying.
NOTION_INVALID_CONFIGAuthentication fields are missing or misconfigured.

Output

Success Port

FieldTypeDescription
idstringDatabase UUID.
titlestringPlain-text database title extracted from the rich-text title array.
urlstringFull Notion URL to open the database in the browser.
created_timestringISO 8601 timestamp of database creation.
last_edited_timestringISO 8601 timestamp of the last edit to the database or any of its pages.
propertiesobjectMap of property name to property schema definition. Each entry includes type and type-specific metadata such as select options or relation targets.
parentobjectParent object describing whether the database is a child of a page or the workspace root.

Error Port

On failure the error port receives an object with errorCode (string), message (string), and httpStatus (number). Use an IfCondition node on the error port to handle NOTION_PERMISSION_DENIED separately from transient errors.

Sample Output

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "title": "Sprint Tasks",
  "url": "https://www.notion.so/a1b2c3d4e5f67890abcdef1234567890",
  "created_time": "2024-01-15T09:00:00.000Z",
  "last_edited_time": "2024-06-10T14:32:00.000Z",
  "properties": {
    "Name": { "id": "title", "type": "title" },
    "Status": {
      "id": "abc1",
      "type": "select",
      "select": {
        "options": [
          { "name": "Backlog", "color": "gray" },
          { "name": "In Progress", "color": "blue" },
          { "name": "Done", "color": "green" }
        ]
      }
    },
    "Assignee": { "id": "abc2", "type": "people" },
    "Due": { "id": "abc3", "type": "date" },
    "Priority": {
      "id": "abc4",
      "type": "select",
      "select": {
        "options": [
          { "name": "High", "color": "red" },
          { "name": "Medium", "color": "yellow" },
          { "name": "Low", "color": "green" }
        ]
      }
    }
  },
  "parent": { "type": "workspace", "workspace": true }
}

Expression Reference

ExpressionTypeExample ValueUse
{{ $output.getDatabase.title }}string"Sprint Tasks"Include database name in notification messages
{{ $output.getDatabase.url }}stringFull Notion URLEmbed as hyperlink in emails or Slack messages
{{ $output.getDatabase.last_edited_time }}string"2024-06-10T14:32:00.000Z"Compare against stored timestamp for incremental sync
{{ $output.getDatabase.properties.Status.select.options }}arrayArray of option objectsPopulate a select dropdown with valid values
{{ $output.getDatabase.id }}stringUUID stringPass to subsequent database/query or database/createPage calls

Node Policies & GuardRails

PolicyDetail
Share integration firstThe integration must be added to each database via Notion's Connections menu before any API call. A missing share results in NOTION_PERMISSION_DENIED — this is not an authentication issue and cannot be fixed by rotating the token.
Use UUIDs, not titlesDatabase titles can be renamed in Notion. Always reference databases by their UUID stored in a workflow variable or credential, never by a hardcoded title string.
Cache schema between runsIf your workflow only creates pages without needing fresh property options, cache the schema output in a workflow variable and skip database/get on subsequent runs. This reduces API calls and avoids rate limiting.
Respect rate limit3 requests/second per integration. When calling database/get inside a loop, add a Delay node (400 ms) between iterations.
Store token in Credentials ManagerNever hard-code ApiToken in workflow configuration. Always reference it as {{ $credentials.notionApiToken }}.
Validate UUID format before callUse a Function node to validate the UUID format (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i) before calling this node to surface config errors early.

Workflow Examples

Example 1 — Incremental Sync Guard

Before querying all pages, check whether the database has been edited since the last sync run. If unchanged, skip the expensive query.

// Step 1: database/get
{
  "resource": "database",
  "operation": "get",
  "Database": "{{ $vars.sprintDbId }}"
}

// Step 2: IfCondition
// Condition: {{ $output.getDatabase.last_edited_time }} > {{ $vars.lastSyncTime }}
// True branch: proceed to database/query
// False branch: StopWorkflow (no changes)

Example 2 — Dynamic Select Options for BizFirst Form

Read the Status property options from the database schema, then pass them to a Form node to build a dropdown dynamically.

// Step 1: database/get
{
  "resource": "database",
  "operation": "get",
  "Database": "{{ $vars.taskDbId }}"
}

// Step 2: Function node — extract option names
const options = $output.getDatabase.properties.Status.select.options.map(o => o.name);
return { statusOptions: options };

// Step 3: Form node — use statusOptions as dropdown choices

Example 3 — Pre-Import Access Check

Before starting a 500-row bulk import, verify access to the database. Fail fast with a clear error message instead of discovering the problem mid-import.

// Step 1: database/get (with error port wired to notification)
// Error port: Slack/postMessage
{
  "channel": "#ops-alerts",
  "text": "Bulk import blocked: {{ $error.message }} ({{ $error.errorCode }})"
}
// Success port: proceed to Loop + database/createPage