Connect Notion with Slack
Implementation Guide
Overview: Connecting Notion and Slack
Notion has matured from a personal note-taking tool into a legitimate enterprise knowledge management and project tracking platform. Engineering teams use Notion databases to track project status, OKRs, incident post-mortems, RFC (Request for Comments) documents, and sprint planning boards. Operations teams use it for runbooks, vendor tracking, and process documentation. Product teams use it for roadmaps, feature specs, and release notes. In all of these contexts, Notion is an asynchronous, pull-based medium: team members need to actively navigate to the Notion workspace to see what has changed. Slack, by contrast, is a push-based, real-time communication medium: critical updates surface in team members' attention streams automatically.
The Notion-to-Slack integration bridges these two interaction models by converting Notion database changes—a project status flipping to "Blocked," a new incident post-mortem page being created, a roadmap item being marked as "Shipped"—into real-time Slack notifications that push relevant information to the people who need to act on it. This is particularly valuable for cross-functional visibility: a product manager creating a new feature spec in Notion can automatically notify the engineering channel; an operations team member marking a vendor as "At Risk" in a vendor tracking database can immediately alert the procurement manager via direct message. The integration does not replace the deep work that happens in Notion—it ensures that the outputs of that deep work reach the teams who depend on them without requiring everyone to maintain constant vigilance over Notion's notification inbox.
Core Prerequisites
On the Notion side, the integration requires access to Notion's API, which is managed through Notion's integration system at notion.so/my-integrations. Create an Internal Integration (for single-workspace use) and retain the Internal Integration Token, which serves as a Bearer token for all API authentication. The integration token must be given explicit access to each Notion database or page it needs to read—navigate to the specific Notion database, click the (...) menu in the top right, select "Add Connections," and add your integration. Without this explicit page/database-level connection, the Notion API will return 404 for any request referencing that resource, regardless of how recently the integration token was generated.
The Notion API does not natively support webhooks as of the current API version. This is the most significant architectural constraint for this integration. Rather than receiving push events when Notion content changes, your integration must poll the Notion API at a defined interval to detect changes. The recommended polling mechanism queries a Notion database for recently modified pages using the filter object on the last_edited_time property, requesting all pages modified after the timestamp of your last successful poll. POST to https://api.notion.com/v1/databases/{databaseId}/query with the Notion-Version: 2022-06-28 header and a request body filtering for recent changes:
{
"filter": {
"timestamp": "last_edited_time",
"last_edited_time": {
"after": "2025-09-15T10:00:00.000Z"
}
},
"sorts": [{ "timestamp": "last_edited_time", "direction": "ascending" }]
}
For iPaaS platforms like Zapier and Make, this polling is handled natively by their Notion trigger modules. For custom backend integrations, implement the polling loop as a scheduled job (cron or cloud scheduler) running at your desired notification latency interval—1-minute polling gives near-real-time behavior but generates more API calls; 5-minute polling is sufficient for most non-critical use cases and stays well within Notion's rate limits.
On the Slack side, the required Bot Token scopes are chat:write, chat:write.public, channels:read, groups:read, and users:read.email for owner-to-Slack-user resolution. If your integration implements a daily digest pattern using Slack's chat.scheduleMessage endpoint, no additional scopes are needed. If you implement Block Kit-based notifications with interactive buttons (e.g., a "Mark as Reviewed" button that updates a Notion page property directly from Slack via a back-action), enable Interactivity in your Slack App and configure the Request URL endpoint to receive interaction payloads.
Top Enterprise Use Cases
The most widely valuable use case is project status change alerting for cross-functional stakeholders. Consider a Notion database used to track engineering projects, with each row representing a project and a "Status" select property with values like "Planning," "In Development," "Blocked," "Shipped," and "Cancelled." When any project's Status transitions to "Blocked," the integration immediately posts a notification to the #project-status Slack channel (or directly to the project lead) including the project name, the previous status, the new status, the Notion page owner's name, and a direct link to the Notion page where the blocking issue is described. This turns Notion's asynchronous status tracking into an active escalation mechanism.
Content publication notification is the second major use case for teams that use Notion as a lightweight CMS or internal knowledge base. When a Notion page's "Published" property (a Checkbox or Select field) is set to true, or when a page's status changes to "Published" or "Approved," the integration posts a formatted summary to the #announcements or #knowledge-base-updates channel: the page title, a one-paragraph description (drawn from the page's first text block), the author, and the direct Notion link. This ensures that newly published runbooks, policy documents, or technical guides reach the team's attention without relying on authors to separately post in Slack after publishing.
OKR and goal tracking is a third use case for leadership and operations teams. Notion databases tracking company-wide or team-level OKRs can trigger weekly or milestone-based digest posts to leadership Slack channels, summarizing progress across all key results: which are on-track, which are at-risk, and which have been completed. Rather than requiring quarterly OKR review meetings to surface this information, the integration makes it available as a regular cadence update in the channel where leadership already operates.
New incident post-mortem creation alerting is a fourth high-value use case for engineering organizations with an SRE or on-call function. When a new page is created in the Incident Post-Mortem Notion database, the integration immediately posts to #incidents with the incident title, severity, date, and a link for team members to contribute to the post-mortem document, ensuring immediate cross-team awareness without a separate Slack message being manually composed by the incident commander.
Step-by-Step Implementation Guide
Since Notion does not provide native webhooks, your middleware must implement a poll-and-diff mechanism. On each poll cycle, query the target Notion database for pages modified since the last poll timestamp. For each returned page, compare the current property values against the previous values stored in your local state cache (a simple key-value store keyed on the Notion page ID). If specific properties have changed—for example, if the Status select property has changed value—emit a change event that triggers the downstream Slack notification logic. This local state caching is essential to avoid posting to Slack on every poll cycle for every page that has merely been touched, not meaningfully changed.
A Notion database query response for a project tracking database looks like this (simplified):
{
"results": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"last_edited_time": "2025-09-15T11:42:00.000Z",
"url": "https://www.notion.so/yourworkspace/PROJ-NAME-a1b2c3d4e5f67890abcdef1234567890",
"properties": {
"Name": {
"type": "title",
"title": [{ "plain_text": "Payment Retry Infrastructure Upgrade" }]
},
"Status": {
"type": "select",
"select": { "name": "Blocked", "color": "red" }
},
"Owner": {
"type": "people",
"people": [{ "name": "Alex Chen", "person": { "email": "[email protected]" } }]
},
"Priority": {
"type": "select",
"select": { "name": "P1" }
}
}
}
]
}
After detecting that the Status property changed from "In Development" to "Blocked" (via your local state cache comparison), your middleware resolves the page owner's Slack user ID using users.lookupByEmail with [email protected] from the Owner.people[0].person.email field. It then constructs a Block Kit payload for chat.postMessage:
{
"channel": "C04XPROJECTSTATUS",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🚨 Project Blocked: Payment Retry Infrastructure Upgrade"
}
},
{
"type": "section",
"fields": [
{ "type": "mrkdwn", "text": "*Status Change:*\nIn Development → Blocked" },
{ "type": "mrkdwn", "text": "*Priority:*\nP1" },
{ "type": "mrkdwn", "text": "*Owner:*\n<@U04XALEXCHEN>" },
{ "type": "mrkdwn", "text": "*Last Edited:*\nSep 15, 2025 at 11:42 AM" }
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": { "type": "plain_text", "text": "View in Notion" },
"url": "https://www.notion.so/yourworkspace/PROJ-NAME-a1b2c3d4..."
}
]
}
]
}
For Zapier, use the Notion "Updated Database Item" trigger, which polls your specified database at the configured interval and fires when any row is updated. Configure a Filter step to restrict triggering to rows where the "Status" field contains your target value (e.g., "Blocked"). Map the Notion page's title, owner name, and URL to a Slack "Send Channel Message" action using Block Kit JSON in the Blocks field. For owner-to-Slack-ID resolution, add a Slack "Find User by Email" action step between the Notion trigger and the Slack message step, using the Notion owner's email from the People property.
For Make, use the Notion "Watch Database Items" module configured with your target database ID. Unlike Zapier, Make allows you to specify which properties to monitor for changes using a filter condition in the module's "Filter" settings. Downstream, use a Slack "Create a Message" module with the blocks parameter populated by a JSON text block. For rich content extraction—such as pulling the first paragraph of the Notion page body to include as a description in the Slack message—add a Notion "Get a Page Content" module between the trigger and the Slack module, parse the results array from the blocks response, find the first paragraph type block, and extract its rich_text[0].plain_text value for use in the Slack message body.
Common Pitfalls & Troubleshooting
A 404 Not Found from the Notion API for a database or page that you know exists is almost universally caused by the integration not having been explicitly added as a connection to that Notion resource. Notion uses a capability-based access model where API integrations must be granted access at the page or database level by a human workspace member—there is no global admin grant that gives an integration access to all content in a workspace. When you add new Notion databases to your integration's scope, you must navigate to each database, open the connections menu, and add the integration. This is the single most common setup error in Notion integrations and is frequently misidentified as an authentication or token issue.
Notion API rate limiting manifests as a 429 Too Many Requests response with a Retry-After header. Notion's limit is approximately three requests per second per integration token. For integrations polling multiple databases in parallel or enriching large query result sets with per-page GET /v1/pages/{id} calls, this limit is easily reached. Serialize your polling jobs rather than running them in parallel, and implement a small delay between API calls in tight loops (200ms between calls stays safely under the 3 req/s limit). For initial historical backfills of large databases (thousands of pages), use Notion's cursor-based pagination with the start_cursor parameter and process pages in sequential batches with inter-batch delays.
Property type mismatches are a frequent source of parsing errors. Notion's API represents different property types (select, multi-select, people, relation, formula, rollup) as different JSON structures under the same property name key. A property named "Status" may be a select type (with a .select.name path) in one database and a status type (with a .status.name path) in another—these are distinct Notion property types with different payload shapes. Always inspect the type field of each property in your Notion API response before accessing the value sub-object, and implement property-type-aware parsing rather than assuming a fixed path. The type field is always present on every Notion property object in API responses.
Empty or null people properties cause user resolution failures when the Owner or Assignee field of a Notion page has no people assigned. Guard against this by checking that properties.Owner.people.length > 0 before attempting to access people[0].person.email. When no owner is assigned, fall back to posting to the general channel without a user mention, or skip the notification entirely depending on your business requirements—never let a null-safety failure cause an unhandled exception that breaks the polling loop and silently stops all subsequent notifications.