Flowgrammer

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 id plus last_edited_time
  • A captured snapshot_id and 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:

  1. page_id
  2. last_edited_time at freeze
  3. snapshot_id, a label you assign for this cycle
  4. optional content_snapshot_hash_or_note from an export or short hash of the controlled section
  5. 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

  1. 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.
  2. Access. Confirm each named reviewer can open the page as a member or invited guest with the right permission. Failures become external_access_denied.
  3. Identity. Capture page id and last_edited_time. Assign a snapshot_id. Refuse in_review if identity is missing.
  4. 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.
  5. Decide. The named reviewer approves, rejects, or requests changes. Write the decision log for that named act.
  6. Remind and escalate. Notify when due_at approaches. After timeout, set pending_escalation and reassign to the escalation approver. Timeout does not approve.
  7. Invalidate stale content. If someone edits the page, a log row that still cites the old last_edited_time or hash is stale_version.
  8. Stop duplicates. A second request for the same page_id|snapshot_id is duplicate. A replayed webhook with the same decision_id is ignored. Write count stays 1.
  9. Handoff. After approve, update the register once. Retry with the same idempotency key.
  10. 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.

  1. Read README.md, then markdown/setup.md.
  2. Walk DatabaseFieldMap and PlanGatesChecklist. Confirm automations, guests, verification, and page-history settings in your tenant before production.
  3. Import csv/decision-evidence-import.csv. Keep the internal names.
  4. Leave Config as_of at 9 September 2026 while you walk Case01 through Case08.
  5. Confirm case 1 does not enter in_review, case 3 does not move to Approved, and case 8 does not write a second time.
  6. 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

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.