Notion Connector Guide

The Notion connector allows Tealfabric workflows to create and read structured workspace content such as database pages and block content. It is ideal for operational documentation, task tracking automations, and knowledge workflows where process state should be visible to teams in Notion.

Document information
FieldValue
Canonical URL/docs/04_connecting-systems/connectors/n/notion
Version (published date)2026-05-08
Tagsconnectors, reference, notion
Connector IDnotion-1.0.0

Notion connector workflow showing secure integration token access, database page creation, filtered reads, and workflow actions based on workspace data.

When to use this connector

Use this connector when workflows need to write structured records into Notion databases or read workspace state to trigger downstream actions. Common use cases include publishing operational runbooks, updating project tracking boards, and synchronizing status from external systems. This integration works best when each workflow maps to a clear database schema and property naming convention.

Prerequisites

Before configuring the integration, create a Notion integration token and share the target pages or databases with that integration. Confirm the database property schema required by your workflow, including title, select, date, and rich text fields. Validate permissions in a test workspace before enabling production write flows.

Configuration

Store these settings in the integration profile:

  • token (required): Notion integration bearer token.
  • base_url (optional): API base URL; default is https://api.notion.com/v1.
  • timeout_seconds (optional): Request timeout in seconds; default is 30.

test validates that token is present and returns Configuration validation failed when it is missing. Other operations (send, receive, sync, batch) do not pre-validate configuration and rely on Notion API responses.

Create database pages with send

Use send to create or update pages in a Notion database. Keep property payloads aligned with the database schema so requests are predictable and easy to maintain.

const baseUrl = "https://api.example.com/api/v1";
const tenantId = "<TENANT_ID>";
const apiKey = "<API_KEY>";

type NotionPageResult = {
  id?: string;
  url?: string;
};

async function createTrackingPage(integrationId: string): Promise<NotionPageResult> {
  const response = await fetch(
    `${baseUrl}/integrations/${encodeURIComponent(integrationId)}/execute`,
    {
      method: "POST",
      headers: {
        "X-API-Key": apiKey,
        "X-Tenant-ID": tenantId,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        operation: "send",
        endpoint: "pages",
        method: "POST",
        data: {
          parent: { database_id: "<ENTITY_ID>" },
          properties: {
            Name: {
              title: [{ text: { content: "Order A-100" } }],
            },
            Status: {
              select: { name: "In Progress" },
            },
            Source: {
              rich_text: [{ text: { content: "Tealfabric" } }],
            },
          },
        },
      }),
    }
  );
  if (!response.ok) throw new Error(`Request failed: ${response.status}`);
  const payload = (await response.json()) as {
    success?: boolean;
    data?: { data?: NotionPageResult; response?: NotionPageResult };
  };
  const page = payload.data?.data ?? payload.data?.response;
  if (!page) throw new Error("Missing Notion page payload");
  return page;
}
curl -X POST "https://api.example.com/api/v1/integrations/<ENTITY_ID>/execute" \
  -H "X-API-Key: <API_KEY>" \
  -H "X-Tenant-ID: <TENANT_ID>" \
  -H "Content-Type: application/json" \
  -d '{
    "operation": "send",
    "endpoint": "pages",
    "method": "POST",
    "data": {
      "parent": { "database_id": "<ENTITY_ID>" },
      "properties": {
        "Name": {
          "title": [{ "text": { "content": "Order A-100" } }]
        },
        "Status": {
          "select": { "name": "In Progress" }
        }
      }
    }
  }'
{
  "success": true,
  "data": {
    "message_count": 1,
    "data": {
      "id": "<ENTITY_ID>",
      "url": "https://www.notion.so/<ENTITY_ID>"
    },
    "response": {
      "id": "<ENTITY_ID>",
      "url": "https://www.notion.so/<ENTITY_ID>"
    }
  }
}

Query databases with send

Notion database queries use POST databases/{database_id}/query. Use send (not receive) with a JSON body for filters and pagination.

const baseUrl = "https://api.example.com/api/v1";
const tenantId = "<TENANT_ID>";
const apiKey = "<API_KEY>";

type NotionRecord = {
  id?: string;
  properties?: Record<string, unknown>;
};

async function queryDoneRecords(integrationId: string): Promise<NotionRecord[]> {
  const response = await fetch(
    `${baseUrl}/integrations/${encodeURIComponent(integrationId)}/execute`,
    {
      method: "POST",
      headers: {
        "X-API-Key": apiKey,
        "X-Tenant-ID": tenantId,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        operation: "send",
        endpoint: "databases",
        resource_id: "<ENTITY_ID>/query",
        method: "POST",
        data: {
          filter: {
            property: "Status",
            select: { equals: "Done" },
          },
          page_size: 20,
        },
      }),
    }
  );
  if (!response.ok) throw new Error(`Request failed: ${response.status}`);
  const payload = (await response.json()) as {
    success?: boolean;
    data?: { data?: { results?: NotionRecord[] } };
  };
  const results = payload.data?.data?.results;
  if (!Array.isArray(results)) throw new Error("Missing Notion query payload");
  return results;
}
curl -X POST "https://api.example.com/api/v1/integrations/<ENTITY_ID>/execute" \
  -H "X-API-Key: <API_KEY>" \
  -H "X-Tenant-ID: <TENANT_ID>" \
  -H "Content-Type: application/json" \
  -d '{
    "operation": "send",
    "endpoint": "databases",
    "resource_id": "<ENTITY_ID>/query",
    "method": "POST",
    "data": {
      "filter": {
        "property": "Status",
        "select": { "equals": "Done" }
      },
      "page_size": 20
    }
  }'
{
  "success": true,
  "data": {
    "message_count": 1,
    "data": {
      "results": [
        {
          "id": "<ENTITY_ID>",
          "properties": {
            "Status": {
              "select": { "name": "Done" }
            }
          }
        }
      ]
    }
  }
}

Read resources with receive

Use receive for GET endpoints such as retrieving a page or listing users. List payloads expose records in data.data (from results or top-level arrays).

Reliability guidance

Notion enforces request limits, so apply queueing and retry logic when batch-processing large datasets. Keep payload generation schema-aware to avoid runtime errors caused by property type mismatches. For long-running sync jobs, use cursors and checkpoints so workflows can resume safely after interruptions.

Related references