UpdateTrigger icon
OutputValues icon
If icon
SlackIncomingWebhook icon
Log icon

Notion Workspace Page Change Audit Sentinel

React to Notion page changes in real time, alert Slack on bulk-edit anomalies, and log the result for compliance and docs teams.

Categories
Business

Diagram unavailable

We could not build the topology for this blueprint. The flow itself is valid, use the YAML on the left to run it.

A policy, runbook, or pricing page that changes without anyone announcing it is easy to miss; Notion's own page history shows who edited what only if someone thinks to check. This blueprint watches a monitored parent page's children continuously and treats a sudden burst of simultaneous edits, the kind an automation bug or a compromised integration token could cause, differently from one person making a routine update.

This blueprint was created by Mohit Jeswani.

How it works

  1. watch_monitored_pages (io.kestra.plugin.notion.page.UpdateTrigger) polls Notion's Search API every 5 minutes, scoped to direct children of parentPageId, and fires one execution per poll that found at least one changed page.
  2. evaluate (io.kestra.plugin.core.output.OutputValues) counts the changed pages in this one poll with a jq filter and classifies a count at or above max_routine_change_count as BULK_EDIT_ANOMALY, otherwise PAGE_CHANGED.
  3. route (io.kestra.plugin.core.flow.If):
    • routine_log: logs the routine edit and the pages involved.
    • alert: posts the anomalous batch size and page titles to Slack.
  4. summary logs the change count and severity for every poll, closing the governance cycle.
  5. errors alerts Slack if the flow itself fails, for example an expired API token.

What you get

  • A real distinction between one person editing a page and a burst of simultaneous changes, instead of a single undifferentiated "something changed" signal.
  • Page titles and links in both the routine log and the anomaly alert, resolved from Notion's own title property, not just an opaque page ID.
  • A read-only check: this flow only observes changes, it never edits or archives a page itself.

Who it's for

  • Compliance, security, and documentation owners who keep policy or runbook pages under one parent page in Notion and want unexpected bulk changes flagged, not discovered weeks later.
  • Teams evaluating the Notion plugin who want a worked example of page.UpdateTrigger used as a standing change-audit sentinel, distinct from this repo's other Notion blueprints, which create or update pages and query a database rather than watching for changes in real time.

Why orchestrate this with Kestra

Notion's own page history shows every edit one page at a time; it does not watch a whole page tree, compare the change rate against what you consider routine, or notify a Slack channel when a burst looks anomalous. Kestra keeps max_routine_change_count as a flow input, turns a bulk-edit burst into a Slack page naming the real pages involved, and keeps a full execution history of every poll that found a change.

Prerequisites

  • A Notion parent page whose direct children you want to monitor, shared with a Notion internal integration.
  • A Notion internal integration token with read access to that page tree.
  • A Slack incoming webhook URL.

This flow calls the Notion API directly over HTTPS; there is no official local Notion emulator, so watch_monitored_pages cannot be exercised against a local Docker container.

Secrets

  • NOTION_API_TOKEN: the Notion internal integration token used by watch_monitored_pages. In Kestra OSS, provide it as the base64-encoded environment variable SECRET_NOTION_API_TOKEN.
  • SLACK_WEBHOOK_URL: used by alert and the errors block. In Kestra OSS, provide it as the base64-encoded environment variable SECRET_SLACK_WEBHOOK_URL.

Inputs

Input Default Purpose
max_routine_change_count 5 Pages-changed-per-poll count at or above which the batch is classified as a bulk-edit anomaly

Outputs

  • {{ trigger.updatedPages }}: the changed pages this poll found (pageId, url, title, lastEditedTime), as UpdateTrigger reports them.
  • {{ outputs.evaluate.values.updated_count }} / .updated_summary: how many pages changed and their titles (or URL/ID when a title cannot be resolved), semicolon-joined.
  • {{ outputs.evaluate.values.severity }}: PAGE_CHANGED or BULK_EDIT_ANOMALY.

Pitfalls

  • parentPageId filtering happens client-side after Notion's Search API call, since the Search API has no server-side parent filter; a page tree with very high edit volume can mean more API calls per poll than the matched-page count alone suggests.
  • last_edited_time has only minute-level precision in Notion's API; two edits inside the same minute as a previous poll's watermark are both still caught, but cannot be distinguished from each other by timestamp alone.
  • This trigger has no inputs: to override per execution, since a polling trigger's own configuration (parentPageId, interval) is set directly on the trigger block, not templated from flow inputs.

Quick start

  1. Add the NOTION_API_TOKEN and SLACK_WEBHOOK_URL secrets, and share the target parent page with your Notion integration.
  2. Set parentPageId on watch_monitored_pages to a real parent page's ID.
  3. Enable the trigger, edit one child page, and confirm routine_log fires.
  4. Edit max_routine_change_count or more child pages within one 5-minute poll window to confirm alert fires as BULK_EDIT_ANOMALY.

How to extend

  • Add a query to watch_monitored_pages to scope the watch to pages matching a keyword, narrowing the search server-side instead of relying on parentPageId alone.
  • Chain io.kestra.plugin.notion.page.Read on each changed page to pull its full content into the alert, not just the title and link.
  • Persist updated_count over successive polls in a KV entry to chart edit-burst frequency over time, not just flag the current poll.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.