Notion Document Approval Workflow
Run a named approve, reject, or request-changes path in Notion, bound to page id, a captured snapshot, and a decision log.
— Craig Major
You can run a named approve, reject, or request-changes path when Notion already holds the request database. Capture the page id and a content snapshot before anyone decides. A decision evidence log, the decision log in this guide, binds each decision to that frozen snapshot so a later edit cannot inherit yesterday’s yes. The Notion Approval Tracker Template gives you the database fields, that log, and eight worked cases.
Start from the published Document Approval Workflow if you still need the shared status and role model. Notion coordinates the request. It is not a native Approvals object and it is not an immutable audit log.
Who this is for
Use this when controlled procedures already live as Notion pages and the missing piece is a named decision on a frozen snapshot.
The download includes:
- Database fields for Documents, Approval Requests, and the decision log
- A setup checklist for permissions, guests, buttons, automations, and page history
- A decision log with page id, snapshot id, last_edited_time, optional content hash, reviewer, and outcome fields
- Eight Cedar & Quay cases, including a missing snapshot, request-changes, unauthorized actor, stale content, guest-access denial, timeout, duplicate request, and a replayed callback
- An original notify-only reminder design. Reminders never change status
You still need one document family for the first build, named reviewers who can open the page as members or invited guests with edit rights, and a place to keep the decision log. Confirm whether your plan includes custom database automations, Send webhook, page verification, and the page-history depth you actually need before you promise those surfaces to a team.
Keep neighboring jobs on their own pages. If you still need to decide what to automate first, use Document Processing Automation. If the pain is unread PDFs, use intelligent document processing. Notion AI can extract information from a database page through Autofill, and imported PDFs can become searchable Notion pages. That is a separate, plan- or credit-dependent document-processing step; it is not part of this approval template. If the job is a money register, use invoice processing automation, the Google Sheets invoice tracker, or the published Notion invoice tracker. If the job is a renewal register, use the contract tracker. If the file lives in Dropbox, use the published Dropbox document approval workflow. If the file lives in Google Drive, use the published Google Drive document approval workflow. If the file lives in Box, use the published Box document approval workflow. E-sign is a separate signature workflow.
What Notion adds to the shared model
The parent owns the control job: closed statuses, named authority, version lock, reminders that never auto-approve, and one idempotent downstream action. This page does not invent a second schema.
Notion implements the coordination half:
- Native page
idpluslast_edited_time - A captured
snapshot_idand optional content hash or export note - Status or Select properties for closed operational states
- Person properties for named requester and approver
- Database buttons and database automations as routing, not as the decision log
- Page comments and mentions as discussion only
- Page verification as a freshness badge, not approve or reject
- Page history as restore, not as the durable record
- Integration webhooks and Send webhook as signals that still need an idempotent handler
The published Document Approval Status and Role Matrix stays the app-neutral field and status list. Parent closed statuses stay 1:1: draft, submitted, in_review, changes_requested, pending_escalation, approved, rejected, cancelled, superseded, duplicate. Notion overlay statuses record access and snapshot exceptions: access_verified, unauthorized_attempt_logged, external_access_denied, missing_snapshot.
Do not copy Drive Approvals lock or reset semantics onto Notion. Drive has a native Approvals object. Notion does not. Do not copy Box file_version onto a Notion page. A captured snapshot is a freeze you make, not a hidden Notion version API.
Page identity and snapshot rule
Prefer native page id over a title or URL. Titles change. Freeze identity when the row enters in_review:
page_idlast_edited_timeat freezesnapshot_id, a label you assign for this cycle- optional
content_snapshot_hash_or_notefrom an export or short hash of the controlled section - page URL as a human pointer only
Refuse in_review if page id or last_edited_time is missing. A later decision that cites a different last_edited_time or hash is stale. Void it. Recapture metadata. Start a new cycle. Do not reuse the prior decision_id as current authority.
Official Notion page history, accessed 9 September 2026, records a version about every 10 minutes while someone edits. Restore depth is plan-gated: Free 7 days, Plus 30 days, Business 90 days, Enterprise unlimited. Restore needs Can edit. Restoring a database restores pages and properties, not the body inside each page. That history helps you recover text. It is not a certified decision ledger.
Buttons, automations, and page history
Notion can add a Button property on every plan. Official Help uses “approve a document” as a click example. That is a convenience pattern. It is not a secure Approvals product. Creating or editing a button needs Full access or Can edit. Clicking needs Full access, Can edit, or Can edit content. Paid plans gate mail and webhook actions. Slack notifications need Plus or higher. A confirmation screen can warn before a property changes. A click still needs a decision-log row.
Database automations sit on paid plans. Free Plan users can create Slack notification automations only. Guests cannot create automations. Triggers include page added, property edited, and a schedule. Actions include edit property, add page, notify, send mail, Send webhook, and Slack. Automations cannot trigger other automations. A person clicking a button can trigger an automation. Locked databases and restricted pages can block a run.
Send webhook is a paid action. It POSTs properties, not page body. Official docs allow up to five webhook actions per automation and do not require authentication on send. Enterprise owners can disable Allow webhooks in automations. A failed webhook pauses the automation.
Integration webhooks are a different surface. They tell a connection that something changed. They do not ship full page content. Comment events need comment read capability. A comment webhook is a collaboration signal, not an approve.
Page verification, on Business and Enterprise, adds a freshness badge and owners. Expiry notifies inbox and email. A blue check is not approved.
Decision log fields
The decision evidence log (DecisionEvidence) is the durable record. After this section, this page calls it the decision log.
Every terminal action writes a row. The reader columns are:
| Field | Job |
|---|---|
| request_id | Stable request key |
| page_id | Notion native id |
| snapshot_id | Frozen cycle label |
| last_edited_time | Frozen edit timestamp |
| content_snapshot_hash_or_note | Optional stronger bind |
| requester | Named person |
| approver | Named matrix actor who decided |
| decision | approve, reject, or request_changes |
| decided_at | When the person decided |
| rationale | Why, required on reject or changes |
| next_action | notify, update_log, hold, or none |
Keep the parent keys beside those columns: decision_id, authority_rule_id, duplicate_key as lower(page_id)|lower(snapshot_id), idempotency_key as request_id|decision_id|action, downstream_write_count as 0 or 1, and access_test_result.
Comments, mentions, button clicks, Status edits, page-history previews, and verification badges may be linked as discussion or freshness refs. They never stand alone as approval evidence.
Comments, buttons, status, guests, and e-sign
A comment that says “looks good” is not approved. A mention is not authority. A Status property anyone with edit access can change is coordination. A button labeled Approve is still a click. Page history can show what changed. It is not a closed decision.
Guests are page-scoped. Can comment is not approve authority. Public web viewers are not authenticated approvers. Official pricing, accessed 9 September 2026, lists a Free guest limit of 10 and unlimited guests on paid plans. Confirm the current table in your tenant. A removed guest or a domain block is external_access_denied. Email alone is not approval.
E-sign is a separate signature request. A Notion approve is not a legal signature. Keep DocuSign or any Sign product on its own policy.
The published Notion invoice tracker is an AP register. Do not reuse that schema as this decision log.
Worked example
Cedar & Quay Fabrication Ltd is a fictional Toronto fabricator. Quality owns supplier procedure DOC-CQ-PROC-014. Jordan Hale is the Quality Coordinator. The page sits in the CQ-Quality-Controlled database. Morgan Reid (Ops Manager) is the named approver. Avery Chen (Quality Lead) is escalation. Riley Patel is an unauthorized teammate.
Jordan confirms Morgan can open the page as a member, captures page_id id:FICTION-NOTION-CQ-PROC-014, last_edited_time 1 September 2026 14:00 UTC, and snapshot_id snap:notion-cq-014-s14, then assigns the request. The decision log stores request REQ-CQ-2026-0930, those keys, optional hash, Morgan, approve, decided_at, rationale, and next_action update_log. Write count stays 1.
That clean path is the baseline write in the Notion Approval Tracker Template. The eight test cases reuse the same document family.
The Notion approval workflow
- Intake. A page lands in the Documents database by create, template, or import. Register
request_id, requester, and a pointer. Do not approve on create. - Access. Confirm each named reviewer can open the page as a member or invited guest with the right permission. Failures become
external_access_denied. - Identity. Capture page id and last_edited_time. Assign a snapshot_id. Refuse in_review if identity is missing.
- Coordinate. Use a Status property, a button, or a database automation if your plan includes it. Record the button or automation id as a channel ref.
- Decide. The named reviewer approves, rejects, or requests changes. Write the decision log for that named act.
- Remind and escalate. Notify when
due_atapproaches. After timeout, setpending_escalationand reassign to the escalation approver. Timeout does not approve. - Invalidate stale content. If someone edits the page, a log row that still cites the old last_edited_time or hash is
stale_version. - Stop duplicates. A second request for the same
page_id|snapshot_idisduplicate. A replayed webhook with the samedecision_idis ignored. Write count stays 1. - Handoff. After approve, update the register once. Retry with the same idempotency key.
- Evidence. Export the log. Keep comments, buttons, automations, verification, and page history as support, not as the decision.
If you later add an integration, official API docs describe about 3 requests per second per connection plus workspace plan-scaled limits, with 429 or 529 and Retry-After. Treat those as retry guidance. A webhook is not a decision-log row.
What can run automatically, and what cannot
| Situation | Automatic action | Person required |
|---|---|---|
| Page added or Status edited | Optional notify | No |
| Reviewer is a Can comment guest only | Stay out of in_review | Owner invites the right permission |
| Named reviewer approves on the frozen snapshot | One register write after the log row | The decision itself |
| Reviewer rejects or requests changes | Hold; wait for a new snapshot | Owner edits and recaptures identity |
| Actor not on the matrix | Exception unauthorized_approver | Process owner. Status stays in_review |
| Decision cites an old last_edited_time | Exception stale_version | Restart on the current snapshot |
| Webhook replay | Ignore the second write | Confirm write count 1 |
| Guest removed or public link only | Status external_access_denied | Replace reviewer |
| SLA missed | Notify, then escalate | Escalation approver. No auto-approve |
| Payment or bank-detail change | Never | Entirely outside |
Human decisions
A person keeps these decisions:
- Approve, reject, or request-changes
- Authority and self-approval conflicts
- Whether a button, Status edit, or automation may be the channel
- Stale-content restarts after an edit
- Duplicate versus canonical request
- Guest or public-link failures
- Escalation after timeout
- Whether e-sign is even in scope, as a separate job
- Payment release and any supplier bank-detail change
A comment can send a person to the page. It cannot approve. A mention cannot approve. A button cannot approve without a decision-log row. A Status edit cannot approve. Page verification cannot approve. Timeout cannot approve.
Failure paths
| Failure | Expected status | Downstream | Payment |
|---|---|---|---|
| Missing page id or last_edited_time | submitted plus missing_snapshot | No start | Blocked |
| Unauthorized actor or comment-as-approve | in_review plus unauthorized_approver | No write | Blocked |
| Stale last_edited_time or hash | stale_version, still in_review | No write | Blocked |
| Duplicate start or replayed webhook | duplicate | No second write | Blocked |
| External or guest access denial | external_access_denied | No decision yet | Blocked |
| Timeout with no person | pending_escalation | No auto-approve | Blocked |
| Button or automation without a log row | Exception button_or_automation_without_contract | No internal approve | Blocked |
| E-sign treated as approve | Exception sign_confused_as_approval | No internal approve | Blocked |
| Any payment or bank-detail change | Forbidden | Forbidden | Forbidden |
Test cases
The eight Cedar & Quay cases are control fixtures. They are not customer results. A clean named approve (REQ-CQ-2026-0930) is the baseline write the later duplicate and replay cases share.
| Case | Input | Without the decision log | After the control |
|---|---|---|---|
| Missing identity or snapshot | Page created, no page_id or last_edited_time | Status flipped on create | submitted plus missing_snapshot |
| Request changes then new snapshot | Morgan asks for section 4 | Old comment looks like progress | New snapshot_id; prior decision not reused |
| Unauthorized approver | Riley clicks Approve | Status flipped by a button | unauthorized_approver; reviewers unchanged |
| Stale content | Approve cites s14 after a new edit | Wrong text marked approved | Void; recapture current last_edited_time |
| External or guest access denial | Supplier has Can comment only | Comment treated as approve | external_access_denied |
| Timeout / escalation | due_at 1 September 2026, as_of 9 September | Silence treated as approve | pending_escalation to Avery Chen |
| Duplicate request | Second row for the same page_id|snapshot_id | Second register write | First write wins; write count 0 |
| Idempotent callback | Webhook replay for the same decision_id | Second register write | First write wins; write count 0 |
The paired template walks each case on its own sheet and in csv/case-recalc.csv.
What to measure
Record requests received, share of rows with page id and snapshot captured, unauthorized attempts blocked, duplicate notifications suppressed, timeouts escalated without auto-approve, access tests passed before in_review, and approvals that have a register or log write ref. Keep those as definitions. Do not invent a benchmark, accuracy rate, or ROI figure.
There is no live production Notion approval metric here, and no Flowgrammer customer Notion case.
How to use the template
Download the Notion Approval Tracker Template. It is an editable workbook and CSV pack, not a Marketplace listing and not a live Notion workspace.
- Read
README.md, thenmarkdown/setup.md. - Walk DatabaseFieldMap and PlanGatesChecklist. Confirm automations, guests, verification, and page-history settings in your tenant before production.
- Import
csv/decision-evidence-import.csv. Keep the internal names. - Leave Config as_of at 9 September 2026 while you walk Case01 through Case08.
- Confirm case 1 does not enter in_review, case 3 does not move to Approved, and case 8 does not write a second time.
- Answer the security, retention, and e-sign-separation questions. Labels and metadata alone are not a records program.
The useful rules also sit on this page so the download is not the only answer. Packaged formulas use the Config as_of date and do not call TODAY().
Realistic limitations
- Notion coordinates the request. It does not become organizational authority or an immutable audit log.
- Custom database automations, Send webhook, Slack notifications, and page verification are plan-gated. Confirm the current official tables in your tenant.
- Page history depth is plan-gated and is restore, not the decision log.
- Guests are page-scoped. Can comment is not approve.
- Sequential multi-step routing is a button or automation overlay, not a second status model.
- API rate limits exist. Recheck official wording before you promise throughput.
- Tenant screens differ. Confirm the screens you actually see.
- This system does not sign a document, pay anyone, run OCR, or change supplier bank details.
- This guide does not rank Notion against Dropbox, Drive, or Box, and it is not an e-sign buyer’s guide.
- This page is operational guidance, not legal advice and not a compliance certification.
Next step
Download the Notion Approval Tracker Template for the database fields, decision log, and eight worked cases.
Keep the parent Document Approval Workflow and Document Approval Status and Role Matrix nearby for the shared model.
If reviewers, authority, or the destination log are still unclear, start with an AI Success Audit. If those pieces are already named, book a fit call to scope an AI Automation Systems build.
Sources
- Flowgrammer, "Document Approval Workflow", Flowgrammer, accessed 2026-09-09, /insights/document-approval-workflow
- Flowgrammer, "Document Approval Status and Role Matrix", Flowgrammer, accessed 2026-09-09, /resources/document-approval-workflow-template
- Flowgrammer, "Document Processing Automation: What to Automate First", Flowgrammer, accessed 2026-09-09, /insights/document-processing-automation
- Flowgrammer, "Intelligent Document Processing (IDP): How It Works", Flowgrammer, accessed 2026-09-09, /insights/intelligent-document-processing
- Flowgrammer, "Invoice Processing Automation: Workflow & Test Pack", Flowgrammer, accessed 2026-09-09, /insights/invoice-processing-automation
- Flowgrammer, "Google Sheets Invoice Tracker", Flowgrammer, accessed 2026-09-09, /insights/google-sheets-invoice-tracker
- Flowgrammer, "Notion Invoice Tracker", Flowgrammer, accessed 2026-09-09, /insights/notion-invoice-tracker
- Flowgrammer, "Contract Tracker", Flowgrammer, accessed 2026-09-09, /insights/contract-tracker
- Flowgrammer, "Dropbox Document Approval Workflow", Flowgrammer, accessed 2026-09-09, /insights/dropbox-document-approval-workflow
- Flowgrammer, "Google Drive Document Approval Workflow", Flowgrammer, accessed 2026-09-09, /insights/google-drive-document-approval-workflow
- Flowgrammer, "Box Document Approval Workflow", Flowgrammer, accessed 2026-09-09, /insights/box-document-approval-workflow
- Notion, "Database automations in Notion", Notion Help, accessed 2026-09-09, https://www.notion.com/help/database-automations
- Notion, "Database buttons in Notion", Notion Help, accessed 2026-09-09, https://www.notion.com/help/database-buttons
- Notion, "Create and use buttons in Notion", Notion Help, accessed 2026-09-09, https://www.notion.com/help/buttons
- Notion, "Webhook actions in Notion automations", Notion Help, accessed 2026-09-09, https://www.notion.com/help/webhook-actions
- Notion, "Delete and restore content in Notion", Notion Help, accessed 2026-09-09, https://www.notion.com/help/duplicate-delete-and-restore-content
- Notion, "Sharing and permissions settings in Notion", Notion Help, accessed 2026-09-09, https://www.notion.com/help/sharing-and-permissions
- Notion, "Share your Notion pages", Notion Help, accessed 2026-09-09, https://www.notion.com/help/share-your-work
- Notion, "Wikis and verified pages in Notion", Notion Help, accessed 2026-09-09, https://www.notion.com/help/wikis-and-verified-pages
- Notion, "Reminders in Notion", Notion Help, accessed 2026-09-09, https://www.notion.com/help/reminders
- Notion, "Request limits", Notion Developers, accessed 2026-09-09, https://developers.notion.com/reference/request-limits
- Notion, "Webhooks", Notion Developers, accessed 2026-09-09, https://developers.notion.com/reference/webhooks
- Notion, "Event types and delivery", Notion Developers, accessed 2026-09-09, https://developers.notion.com/reference/webhooks-events-delivery
- Notion, "Notion Pricing Plans", Notion, accessed 2026-09-09, https://www.notion.com/pricing
- Notion, "Notion AI FAQs", Notion Help, accessed 2026-09-09, https://www.notion.com/help/notion-ai-faqs
- Notion, "Autofill", Notion Help, accessed 2026-09-09, https://www.notion.com/en-gb/help/autofill
- Notion, "Import data into Notion", Notion Help, accessed 2026-09-09, https://www.notion.com/help/import-data-into-notion
Frequently asked questions
What is a Notion document approval workflow?
It is a named approve, reject, or request-changes path for a page that already lives in Notion, bound to page id and a captured snapshot, and written to a decision log. Buttons and automations can coordinate the work. Comments, mentions, Status edits, page verification, page history, and e-sign are not the decision.
Do Notion buttons replace the decision log?
No. A button can edit a property or send a notification. The decision log still stores the named actor, timestamp, page id, snapshot, and outcome.
Are database automations enough?
Automations can notify or edit properties on paid plans. Free Plan users can create Slack notification automations only. Guests cannot create automations. An automation run becomes evidence only when the decision log also stores the named act.
Are comments or page history enough?
No. Comments are collaboration. Page history is restore with a plan-gated window. Keep a named decision row.
Does a later edit cancel an approval?
A decision that still cites the old last_edited_time or hash is stale. Void it and start a new cycle on the current snapshot.
Is this the same as the Notion invoice tracker?
No. The published [Notion invoice tracker](/insights/notion-invoice-tracker) is an AP register. This page is a document approval coordination path.
Can timeout auto-approve?
No. Timeout becomes pending_escalation. A person still decides.