Skip to main content

Overview

When a user installs a public integration, the integration often needs to set up structured content in the user’s workspace — for example, a project tracker database with pre-configured views, or a set of pages with a specific layout. Historically, the recommended approach was to configure a template URL during OAuth. When a user authorizes the integration, they can choose to duplicate a static Notion page into their workspace. While this works, it has limitations:
  • The template is a snapshot — it can’t adapt to the user’s existing workspace or preferences.
  • The integration can’t customize the setup based on user choices during onboarding.
  • The duplicated content may include boilerplate that doesn’t apply to every user.
A more flexible approach is to build the workspace content programmatically using the API. Public integrations can create top-level pages and databases directly in a user’s workspace, configure views, and apply templates — all tailored to what the user actually needs.

Create workspace-level pages and databases.

Configure views on new databases.

Populate pages using templates.

How it works

The end-to-end flow looks like this:
1

User authorizes your integration

The user goes through the standard OAuth flow. You receive an access_token with the capabilities your integration requested. No template URL is needed.
2

Create databases and pages

Use the API to create databases and pages at the workspace level. These appear in the user’s Private section in Notion.
3

Configure views

Use the views API to add views (table, board, calendar, etc.) to your newly created databases, with the filters and sorts your integration needs.
4

Optionally apply templates

If your databases have data source templates, you can create pages that start from those templates for a richer initial experience.

Creating workspace-level content

Public integrations can create pages and databases at the workspace level by omitting the parent parameter (or setting it to { "type": "workspace", "workspace": true }). This places the content in the authorizing user’s Private section.
This capability is only available to public integrations. Internal integrations cannot create workspace-level content because they aren’t owned by a single user.

Create a database

curl -X POST https://api.notion.com/v1/databases \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Content-Type: application/json" \
  -H "Notion-Version: 2026-03-11" \
  --data '{
    "title": [{ "type": "text", "text": { "content": "Project Tracker" } }],
    "is_inline": false,
    "initial_data_source": {
      "properties": {
        "Task": { "title": {} },
        "Status": {
          "status": {
            "options": [
              { "name": "Not started", "color": "default" },
              { "name": "In progress", "color": "blue" },
              { "name": "Done", "color": "green" }
            ]
          }
        },
        "Assignee": { "people": {} },
        "Due date": { "date": {} }
      }
    }
  }'
The new database is created with one data source and one default Table view. Store the database.id and database.data_sources[0].id — you’ll need them to create views and pages.

Create a standalone page

curl -X POST https://api.notion.com/v1/pages \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Content-Type: application/json" \
  -H "Notion-Version: 2026-03-11" \
  --data '{
    "properties": {
      "title": {
        "title": [{ "type": "text", "text": { "content": "Getting Started" } }]
      }
    },
    "children": [
      {
        "object": "block",
        "type": "heading_2",
        "heading_2": {
          "rich_text": [{ "type": "text", "text": { "content": "Welcome!" } }]
        }
      },
      {
        "object": "block",
        "type": "paragraph",
        "paragraph": {
          "rich_text": [
            { "type": "text", "text": { "content": "This page was created by your integration. You can move it anywhere in your workspace." } }
          ]
        }
      }
    ]
  }'

Adding views

After creating a database, you can add views that match your integration’s use cases. Each database starts with a default Table view, but you’ll likely want to create additional views with specific filters, sorts, and layout types. For a project tracker, you might want a Board view grouped by status and a Calendar view for due dates:
# Board view: tasks grouped by status
curl -X POST https://api.notion.com/v1/views \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Content-Type: application/json" \
  -H "Notion-Version: 2026-03-11" \
  --data '{
    "database_id": "DATABASE_ID",
    "data_source_id": "DATA_SOURCE_ID",
    "name": "Task board",
    "type": "board"
  }'

# Calendar view: tasks by due date
curl -X POST https://api.notion.com/v1/views \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Content-Type: application/json" \
  -H "Notion-Version: 2026-03-11" \
  --data '{
    "database_id": "DATABASE_ID",
    "data_source_id": "DATA_SOURCE_ID",
    "name": "Schedule",
    "type": "calendar"
  }'

# Filtered table: only active tasks
curl -X POST https://api.notion.com/v1/views \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Content-Type: application/json" \
  -H "Notion-Version: 2026-03-11" \
  --data '{
    "database_id": "DATABASE_ID",
    "data_source_id": "DATA_SOURCE_ID",
    "name": "Active tasks",
    "type": "table",
    "filter": {
      "property": "Status",
      "status": {
        "does_not_equal": "Done"
      }
    },
    "sorts": [
      {
        "property": "Due date",
        "direction": "ascending"
      }
    ]
  }'
See the Working with views guide for full details on creating, updating, and querying views.

Applying templates

If your integration pre-configures database templates for the data source, you can create pages that start from those templates. This is useful for providing users with structured starting points — for example, a “Bug report” template with pre-filled sections.
# List available templates for the data source
curl -X GET "https://api.notion.com/v1/data_sources/DATA_SOURCE_ID/templates" \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Notion-Version: 2026-03-11"

# Create a page using the default template
curl -X POST https://api.notion.com/v1/pages \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Content-Type: application/json" \
  -H "Notion-Version: 2026-03-11" \
  --data '{
    "parent": {
      "type": "data_source_id",
      "data_source_id": "DATA_SOURCE_ID"
    },
    "properties": {
      "Task": {
        "title": [{ "type": "text", "text": { "content": "My first task" } }]
      }
    },
    "template": {
      "type": "default"
    }
  }'
Template content is applied asynchronously after the page is created. If your integration needs to take action once the template is fully applied, use webhooks to listen for page.content_updated events. See the Creating pages from templates guide for the full workflow.

Comparison with template URL duplication

Template URL (OAuth)Programmatic setup
CustomizationStatic — every user gets the same templateDynamic — tailor content to each user
Schema controlTemplate is a snapshot; schema changes require updating the source pageFull control over database schema, properties, and views at creation time
Multiple databasesOne template page per integrationCreate as many databases and pages as needed
View configurationViews are duplicated as-is from the templateCreate views programmatically with specific filters, sorts, and types
User interactionUser must choose “Duplicate template” during OAuthNo extra user interaction needed — setup happens after authorization
Template URL still useful?Yes — it’s simpler for basic use cases where a static template is sufficientUse programmatic setup when you need flexibility or when the template URL approach doesn’t cover your needs
The template URL approach still works and is fine for simple integrations. Programmatic setup is recommended when your integration needs to create multiple resources, customize content per user, or keep the workspace structure in sync with external systems over time.

Next steps