
Use Notion Verification as a second gate before n8n ships a release
Build a two-gate Notion-to-n8n release workflow that re-reads the page, blocks unverified or expired content, and marks the page Released only after the downstream action succeeds.
The trick: make Verification the second gate
Use two independent gates before a release workflow can act:
Notion Status = Ready for release
-> database automation sends page ID to n8n
-> n8n GETs the page
-> Verification = verified ?
yes -> call the release action -> PATCH Status = Released
no -> PATCH Status = Needs verificationThe first gate says the PM is asking the workflow to run. The second gate says the page passed the team’s factual review. The important detail is that the check happens in n8n against the page returned by the API, not in a prompt, a view filter, or a value copied into the webhook payload.
Notion’s
Verification property is available on pages in a wiki database. Its API value can be verified, unverified, or expired; the API also records the connection that performed a write as verified_by. 1This pattern is useful for launch briefs, customer-facing changelogs, security runbooks, and any document that must be reviewed before another system distributes it. The example below sends a verified release packet to a downstream HTTP endpoint. Replace that endpoint with your Slack, Jira, Linear, or internal release action.
Before you build it
Prepare these pieces first:
- A paid Notion plan with database automations. Notion’s webhook action is available in paid-plan automations, including database automations. 2
- A wiki database. The
Verificationproperty is not a general-purpose property for every database; the API documentation limits it to pages in a wiki database. 1 - A release data source with a title, a
Release statusStatus property, and aVerificationproperty. Add any fields that the downstream system needs, such asRelease URL,Owner, andAudience. - An n8n workflow with a Webhook trigger, HTTP Request nodes, an IF or Switch node, and the credentials for the downstream action.
- A Notion internal connection shared with the wiki database. It needs read access for the GET request and update access for the final PATCH. A page retrieval without read access can return
403; a missing page or inaccessible page can return404. 3
Use this header on every Notion HTTP Request node:
Authorization: Bearer $NOTION_API_KEY
Notion-Version: 2026-03-11
Content-Type: application/jsonModel the two-gate state machine
Create these properties in the wiki database:
| Property | Type | Values | Owner |
|---|---|---|---|
Name | Title | Release packet title | PM |
Release status | Status | Draft, Ready for release, Needs verification, Released, Blocked | PM and automation |
Verification | Verification | verified, unverified, or expired | Reviewer / Notion API |
Release URL | URL | Destination after release | Automation |
Last release attempt | Date | Timestamp of the last n8n run | Automation |
Release error | Rich text | Short failure reason | Automation |
Keep
Ready for release and verified separate. A PM can finish the content workflow before a reviewer has checked the facts. If the same property represents both ideas, a later edit can accidentally look like approval.The state transitions should be explicit:
Draft -> Ready for release -> Released
\-> Needs verification
\-> BlockedDo not make the n8n worker set
Verification = verified. A verification write records the acting connection as the verifier, so the connection should only write that state when the workflow is intentionally the reviewing authority. 1Configure the Notion trigger
1. Trigger on the human-owned status
Open the wiki database and choose Automations from the lightning icon. Create a new automation with:
Trigger: Property edited
Property: Release status
Condition: is set to Ready for releaseNotion documents
Page added, Property edited, and recurring triggers. A property-edited trigger can target specific database properties, and a status condition is more precise than reacting to every edit. 5The trigger belongs on
Release status, not Verification. That keeps the reviewer’s decision out of the automation’s trigger loop and makes the requested action explicit.2. Send only a routing envelope
Add the Send webhook action. Set the n8n production webhook URL and add a custom header:
X-Workflow-Key: <long-random-secret>Select these database properties for the webhook body:
Name
Release status
Verification
Release URLThe webhook action sends an HTTP
POST, and a database automation can include selected page properties in the payload. It cannot send page contents. The action itself does not require authentication, so use a secret custom header and validate it in n8n. 2Treat the payload as a routing hint, not as the source of truth. The body can be stale by the time n8n receives it, and the workflow needs the page ID to retrieve the current property values. If the automation UI does not expose the page ID as a selectable field, send a dedicated text or URL property containing the page ID, or use the page URL if your n8n expression extracts the ID consistently.
3. Make the automation observable
Add a second action only if your plan and workflow need it; otherwise keep the automation to one webhook. Notion supports up to five webhook actions per automation. If a webhook fails, Notion shows an exclamation mark and pauses the automation until someone resumes it. 2
Build the n8n workflow
The n8n path is deliberately read-before-act:
Webhook
-> validate X-Workflow-Key
-> normalize pageId
-> GET Notion page
-> IF Verification.state == verified
true -> downstream release action -> PATCH Released
false -> PATCH Needs verification1. Receive and normalize
Configure the Webhook node to accept
POST. The first Code or Set node should create a small internal object:{
"pageId": "{{$json.pageId}}",
"receivedAt": "{{$now.toISO()}}",
"attemptId": "{{$execution.id}}"
}Use the exact page ID from your payload mapping. Do not use the title as an identifier: two release packets can have the same title, while the page ID is stable for the page.
Reject the request when
X-Workflow-Key is missing or does not match the n8n credential. Return a successful response only after the event has been accepted into the workflow’s execution path; do not let the downstream release call determine whether the webhook receiver can acknowledge the request.2. Re-read the page
Add an HTTP Request node:
GET https://api.notion.com/v1/pages/{{$json.pageId}}Use the header block from the prerequisite section. The response contains the page properties but not the page content. That is enough for this gate because the decision depends on
Verification and Release status, not on the body of the page. 3Add an IF node with this expression:
{{$json.properties.Verification.verification.state}} == "verified"Your property key may be a property ID rather than the visible name if the database schema uses IDs. Inspect one GET response in n8n and use the returned key exactly.
The false branch covers both
unverified and expired. If you need to distinguish the two for reviewer messaging, use a Switch node with these cases:verified
unverified
expiredThe page property endpoint documents
expired as a returned state when the verification end date is in the past. 13. Stop the unsafe branch
On
unverified or expired, do not call the downstream release system. Add an HTTP Request node to record the reason in Notion:PATCH https://api.notion.com/v1/pages/{{$json.pageId}}{
"properties": {
"Release status": {
"status": { "name": "Needs verification" }
},
"Release error": {
"rich_text": [
{
"text": {
"content": "Verification is unverified or expired; release was not sent."
}
}
]
}
}
}The Update Page API accepts a
properties object, provided the values match the parent data source schema. 44. Release only the verified branch
On
verified, call the downstream action. For a generic HTTP endpoint, use an n8n HTTP Request node with a body such as:{
"pageId": "{{$json.id}}",
"title": "{{$json.properties.Name.title[0].plain_text}}",
"releaseUrl": "{{$json.properties['Release URL'].url}}",
"verificationState": "verified",
"attemptId": "{{$execution.id}}"
}Give the downstream endpoint an idempotency key such as
notion-release/{{$json.id}}. The key prevents a retry from distributing the same release twice. If the downstream system returns a success response, continue to the write-back node. If it returns a failure, keep the page in Ready for release or move it to Blocked and write the error; do not mark it Released.Then PATCH the page:
PATCH https://api.notion.com/v1/pages/{{$json.id}}{
"properties": {
"Release status": {
"status": { "name": "Released" }
},
"Last release attempt": {
"date": { "start": "{{$now.toISO()}}" }
},
"Release error": {
"rich_text": []
}
}
}A page PATCH updates properties on the existing page; it does not change the page’s parent. 4
If the
Released write itself triggers the same Notion automation, the condition is set to Ready for release prevents a loop. Keep that condition exact. A generic is edited trigger will fire again when n8n writes Last release attempt or Release error.Test the gate before connecting a real release action
| Test input | Expected n8n behavior | Expected Notion result |
|---|---|---|
Ready for release + verified | Call downstream action once | Released, timestamp set |
Ready for release + unverified | Skip downstream action | Needs verification, reason written |
Ready for release + expired | Skip downstream action | Needs verification, reason written |
Bad X-Workflow-Key | Reject before the Notion GET | No page change |
Downstream 4xx or 5xx | Do not run success PATCH | Blocked or remain Ready for release |
| Replay of the same webhook | Reuse the page ID and idempotency key | No second release |
| Page deleted or connection unshared | Stop at GET | No release; alert the workflow owner |
Use a test endpoint or a dry-run branch for the first verified case. Then run the unverified and expired cases with a real page, because those are the branches that protect the team from an accidental release.
Gotchas
Verification is a wiki-database feature. If the property is missing from the schema, confirm that the parent database is a wiki database before debugging the API payload. 1
The webhook is not authenticated by default. A custom header is only useful when n8n validates it before the first Notion call. Rotate the key if the workflow URL or execution logs expose it. 2
The webhook does not carry page content. This is a feature, not a reason to send the whole page through the trigger. Read the page after the event and request only the properties needed for the gate. 23
Expired is not verified. A page that was once verified can return
expired after its verification end date. Branch on the current state, not on the existence of a verification date. 1Do not write
Released before the downstream call succeeds. The Notion page is the audit surface. It should describe what the external system actually accepted, not what the worker intended to send.Keep the worker’s writes out of the trigger condition. Trigger on
Release status is set to Ready for release, then write Released, Needs verification, or Blocked. This makes retries visible without creating a self-triggering loop. 5References
- 1Page properties | Notion Docs
developers.notion.com
- 2
- 3Retrieve a page | Notion Docs
developers.notion.com
- 4Update page | Notion Docs
developers.notion.com
- 5
This story was produced automatically by a channel. One sentence is all it takes for Neodrop to keep producing for you.
