The Business Situation

This representative case study follows an 85-person industrial equipment assembly and field service company. The company has a small IT and infrastructure function, an operations department, a manufacturing team, and technicians who work from customer sites.

The knowledge management process is coordinated by an operations systems analyst. Eight subject-matter owners maintain content covering manufacturing procedures, equipment troubleshooting, safety administration, IT support, quality control, and field service. Four department managers act as reviewers. An IT administrator manages access, integrations, and security.

The company has approximately 180 existing procedures, answers, checklists, and templates. These are distributed across Google Drive folders, Slack conversations, individual documents, and local files. Staff create approximately 25 new knowledge submissions per month. Another 28 articles become due for review, while employees submit about 12 corrections or feedback items.

Employees often know that an answer exists but cannot reliably locate the current version. Search terms vary, Slack messages lack ownership and review dates, and duplicate documents remain available after a process changes. The business needs a searchable knowledge base that distinguishes drafts from approved guidance and makes content maintenance measurable.

Note: This case study is provided as a representative example of the types of AI integration and digital transformation solutions Intelligex designs and delivers. Actual engagements are tailored to each client’s goals, constraints, existing systems, timeline, and available resources, so the approach, tools, and outcomes may vary.

The Existing Process

The original process depended on individual employees recognizing that useful information should be documented. There was no consistent intake, assignment, approval, or retirement workflow.

  1. An employee searched Google Drive and Slack using words they expected the original author to have used.
  2. If the answer was not found, the employee asked a colleague or posted the question in a Slack channel.
  3. A subject-matter expert replied in Slack, sent an existing document, or wrote a new procedure.
  4. The document was saved in whichever folder the author normally used. File names and folder structures varied by department.
  5. The author emailed or messaged a manager for informal review. Some content was used before the review was complete.
  6. Corrections were posted in Slack or made directly in a document. There was no dependable connection between the correction and the published article.
  7. Old documents remained searchable because there was no assigned owner, review date, retirement status, or replacement link.

Process Weaknesses

  • Duplicate documents contained different instructions.
  • Important answers remained inside message threads.
  • Ownership depended on personal knowledge.
  • Review requests were followed up manually.
  • Employees could not distinguish drafts from approved guidance.
  • Binary templates and written instructions were stored separately without dependable links.

Business Effects

  • Technicians spent time asking questions that had already been answered.
  • Supervisors repeatedly reviewed similar content.
  • Outdated procedures could remain in active use.
  • New employees received inconsistent instructions.
  • Operations managers lacked data about content volume, age, ownership, and overdue reviews.
  • Knowledge maintenance depended heavily on a few experienced employees.

The absence of a formal audit trail was also important. The company could see when a Google document was edited, but it could not easily show why an article was approved, which review interval applied, who owned the next review, or when obsolete guidance was retired.

What the New System Needed to Do

The team defined business requirements before selecting a platform. The goal was not merely to move documents into a new location. The new process had to govern the complete content lifecycle.

Knowledge base business and technical requirements
Requirement Implementation expectation
Structured intake Collect a title, article type, category, business need, draft content, source links, sensitivity, and urgency.
Unique identity Assign every submission a stable Knowledge ID and prevent duplicate processing when an automation retries.
Ownership Assign an owner and reviewer from a controlled category mapping rather than relying on free-text names.
Publication control Keep draft, review, approved, published, review-due, rejected, and retired content distinct.
Search Support keyword search, category views, article types, aliases, equipment identifiers, and related links.
Permissions Allow broad reading access while limiting editing, review, publishing, and restricted content access.
Review dates Apply 90, 180, or 365-day review intervals and notify owners when content is due.
Feedback Connect employee feedback to the correct article and escalate urgent corrections.
File management Store written guidance in Notion while linking controlled binary templates from Google Drive.
Audit evidence Record review events, publication dates, retirement reasons, automation runs, and errors.
Monitoring Expose overdue articles, incomplete records, failed automation, unassigned content, and recent changes.
Manual override Allow the knowledge coordinator to reassign, correct, republish, or retire an article with a recorded reason.
Human review Prevent automation or AI from independently publishing operational instructions.

Implementation Approaches Considered

Comparison of knowledge base implementation approaches
Approach Connected tools Strengths Limitations Fit for this scenario
Structured Google Drive library Google Drive, Sheets, Forms, Apps Script Retains familiar tools and existing files. Metadata, lifecycle views, page relationships, and publishing controls require substantial custom work. Possible, but weak for article-centric browsing and governance.
Google Sites portal Google Sites, Drive, Forms, Apps Script or Zapier Simple internal publishing and familiar Google access controls. Submission workflow, structured metadata, review queues, and per-record automation need supporting systems. Suitable for a curated portal, but not selected as the primary system of record.
Notion knowledge base Notion, Google Forms, Zapier, Slack Combines pages, structured properties, relations, filtered views, and searchable content. Requires deliberate permission design, lifecycle rules, and API-aware automation. Selected because it balanced usability and governance at the expected volume.
SharePoint knowledge solution SharePoint, Microsoft Lists, Power Automate, Teams Strong Microsoft 365 alignment, document controls, permissions, and enterprise administration. The business did not use Microsoft 365 as its primary productivity environment. A strong option for a Microsoft-centered organization, but it would duplicate the existing stack here.
Dedicated knowledge management platform Specialized knowledge software and application integrations May provide advanced analytics, verification workflows, and support features. Higher implementation overhead and more platform change than the current volume justified. Reserved as an upgrade path if governance or scale becomes more complex.

Improving the existing library would have reduced some naming inconsistency, but it would not have solved ownership, review scheduling, feedback routing, or publication status without a separate metadata application.

Google Sites was considered as a publishing layer. It worked well for curated navigation, but the team would still have needed another structured system to manage article records and review dates.

SharePoint was technically credible. It was not selected because Google Workspace and Slack were already embedded in daily work, and adopting Microsoft 365 solely for the knowledge base would have added administration and duplicate identity management.

Notion offered structured data sources and readable pages in the same environment. Zapier could connect intake, notifications, scheduled checks, and feedback without building a full custom application.

The Selected Solution

The selected solution uses Notion as the knowledge system of record and publishing environment. Google Forms provides controlled intake and article feedback. Zapier validates data, creates and updates records, schedules review checks, and coordinates system-to-system activity. Slack carries actionable notifications without becoming the authoritative content store.

Selected tools and responsibilities
Tool Responsibility
Google Forms New knowledge submissions and employee feedback.
Google Sheets Restricted response storage created by Google Forms and a recovery source for incomplete automation runs.
Notion Knowledge article records, page content, workflow status, ownership, review dates, feedback relations, and operational views.
Zapier Validation, category-to-owner mapping, API calls, record creation, status updates, reminders, escalation, and error notifications.
Slack Reviewer notifications, owner reminders, urgent feedback alerts, and automation failure alerts.
Google Drive Existing controlled templates and binary files that should not be recreated as Notion page content.

Existing Google Workspace and Slack accounts were retained. Written instructions were migrated into Notion pages, while spreadsheets, editable templates, diagrams, and large files remained in controlled Google Drive folders. Notion records link to those files and identify the responsible owner.

Manual copying between the intake form, document index, and review message was removed. Manual reminder tracking was replaced with scheduled checks. Human reviewers retained authority over publication, rejection, operational accuracy, sensitivity classification, and retirement.

System Architecture and Data Flow

  • Intake: Google Forms for article submissions and feedback.
  • System of record: Notion knowledge, feedback, review log, and automation error data sources.
  • Automation layer: Zapier triggers, filters, lookup tables, code steps, paths, loops, and scheduled runs.
  • Document storage: Notion pages for knowledge content and Google Drive for controlled binary files.
  • Notifications: Slack direct messages and designated operational channels.
  • Reporting: Notion filtered views, review metrics, and Zapier run history.
  • AI layer: Optional classification and completeness suggestions after the core workflow is stable.
  1. Submission: An employee completes the Google Form. Google validates required questions and records the response in a restricted response sheet. Zapier receives the response event and a stable response identifier.

  2. Validation and assignment: Zapier checks required fields, normalizes category values, and uses a lookup table to assign the content owner, reviewer, and review interval. Invalid or unmapped submissions are routed to manual review.

  3. Duplicate check: A deterministic Knowledge ID is generated. Before creating a page, the automation queries Notion for that identifier. An existing record is returned instead of creating a duplicate.

  4. Draft creation: Zapier creates a structured Notion record and page body. Notion returns a page ID and URL. These identifiers are used by later steps and are written to the automation output.

  5. Review notification: Zapier posts a Slack notification containing the Knowledge ID, title, category, owner, reviewer, sensitivity, and Notion link. If the reviewer cannot be found, the message goes to the knowledge operations channel.

  6. Approval and publication: The owner prepares the article and moves it to review. The reviewer either requests changes, rejects it, or approves it. An approval event triggers Zapier to set publication dates, update status, create a review log entry, and post confirmation.

  7. Scheduled review: A daily Zap queries Notion for published or review-due articles. It compares each calculated review date with the current date, applies reminder spacing, and routes due records through Slack and Notion update steps.

  8. Feedback: An employee opens the article-specific feedback link. Zapier finds the Notion article by Knowledge ID, creates a related feedback record, and alerts the owner when the correction is urgent.

  9. Failure path: Validation failures remain visible in the Form response sheet. API, authentication, Slack, or update failures generate Zapier error alerts and are recorded in the automation error queue for recovery.

Data Structure

The primary Notion data source is named Knowledge Articles. Property names are kept stable because the API and Zapier mappings depend on them.

Knowledge Articles data structure
Field Type Required Source or allowed values Purpose
Name Title Yes Google Form, maximum 200 characters Human-readable article title.
Knowledge ID Text Yes Automation, format KB-YYYYMMDD-NNNN Stable identifier and idempotency key.
Article Type Select Yes Procedure, Troubleshooting, Answer, Policy, Template, Checklist Controls navigation and review expectations.
Category Select Yes Manufacturing, Field Service, Quality, IT, Safety, General Operations Drives ownership and filtered views.
Summary Text Yes Submitter or owner, maximum 500 characters Search result context and article introduction.
Search Aliases Text No Owner-maintained keywords Captures equipment names, abbreviations, and alternative terms.
Status Select Yes Draft, In Review, Changes Requested, Approved, Published, Review Due, Rejected, Retirement Requested, Retired Controls the article lifecycle.
Priority Select Yes Normal, High, Urgent Controls review timing and escalation.
Submitter Email Email Yes Authenticated Form response Supports clarification and confirmation.
Owner Email Email Yes Category lookup table Identifies the person maintaining content.
Reviewer Email Email Yes Category lookup table Identifies the publication approver.
Sensitivity Select Yes Internal, Restricted Determines the destination data source and permissions.
Submission Date Date and time Yes Google Forms timestamp Preserves original intake time.
Published Date Date No Publication automation Shows first publication date.
Last Reviewed Date No Approval automation Starts the review interval.
Review Interval Days Number Yes 90, 180, or 365 Defines review frequency.
Next Review Date Formula No Last Reviewed plus Review Interval Days Drives reminders and overdue views.
Last Reminder Date No Scheduled automation Prevents daily duplicate reminders.
Source Links Text No One approved HTTPS link per line Links source material and controlled files.
Feedback URL URL Yes after creation Zapier-generated prefilled Form URL Connects feedback to the Knowledge ID.
Replacement Article Relation No Another Knowledge Articles record Directs readers away from retired guidance.
Published By Email No Approval event Records responsible reviewer.
Retired Date Date No Retirement workflow Supports retention and archive rules.
Retirement Reason Text Required for retirement Reviewer Explains why content is no longer active.
Slack Message ID Text No Slack action result Links the original review notification to the record.
Automation Status Select Yes Pending, Processing, OK, Manual Review, Failed Separates workflow status from technical status.
Last Automation Run Date and time No Zapier Supports operational monitoring.
Retry Count Number Yes Automation, default 0 Shows repeated processing attempts.
Error Message Text No Automation Provides a concise recovery message without credentials.
Created Time Created time Automatic Notion System audit field.
Last Updated Last edited time Automatic Notion Supports change monitoring.

The company created a separate Restricted Knowledge Articles data source with the same schema. This avoids relying on fragile per-page exceptions when sensitive runbooks should only be visible to IT administrators and designated managers.

Related Notion data sources
Data source Important fields Relationship
Knowledge Feedback Feedback ID, Article relation, Knowledge ID, Helpful, Comment, Urgency, Submitter, Status, Created Time Many feedback records can relate to one article.
Review Log Review Event Key, Article relation, Previous Status, Decision, Reviewer, Review Date, Notes, Automation Run ID Many review events can relate to one article.
Automation Errors Error ID, Workflow, Record ID, Step, Error Class, Message, Occurred At, Retry Count, Resolution Status, Owner An error optionally relates to an article or feedback record.
Category Ownership Category, Owner Email, Reviewer Email, Review Interval, Slack Channel, Active Used as the controlled assignment reference.

The category, type, status, priority, and sensitivity options must be created before activating automation. Select labels should not be renamed without updating API validation, Zapier filters, views, and documentation.

Workflow Statuses and Ownership

Knowledge article workflow stages
Status Meaning and owner Entry and exit conditions Reminder and escalation
Draft Submission exists but is not approved. Content owner is responsible. Created from intake. Exits when the owner submits it for review. Reminder after three business days. Escalate after seven.
In Review Reviewer checks accuracy, clarity, links, sensitivity, and duplication. Owner confirms required fields and page content are ready. Reviewer reminder after two business days. Escalate after five.
Changes Requested Owner must address specific reviewer comments. Reviewer returns the article with notes. Exits when resubmitted. Owner reminder after three business days.
Approved Human review is complete, but publication automation has not finished. Reviewer records approval. Automation should move it to Published. Technical alert if it remains Approved for more than one hour.
Published Approved guidance available to the intended audience. Publication automation records dates and review evidence. No reminder until the next review date approaches.
Review Due Published content has reached its scheduled review date. Daily automation identifies the due date. Exits through a new review and approval. Owner at due date, reviewer at seven days overdue, manager at fourteen days.
Rejected Submission is unsuitable, duplicative, unsupported, or outside scope. Reviewer supplies a reason. Reopening requires coordinator action. No recurring reminder.
Retirement Requested Owner believes the article is obsolete or replaced. Requires reason and replacement article where applicable. Reviewer reminder after two business days.
Retired Article is no longer active and is excluded from normal published views. Reviewer confirms retirement. Permanent deletion is an administrator decision. Annual retention review.

An article can move backward from In Review to Changes Requested. Rejection requires a reviewer and reason. Retirement requires human confirmation because an automation cannot determine whether operational guidance is still required for historical, contractual, safety, or audit purposes.

Step-by-Step Implementation

Step 1: Prepare the Accounts and Permissions

  1. Confirm that the company has managed Google Workspace, Notion, Zapier, and Slack accounts with the features required for internal forms, workspace access controls, integrations, scheduled automation, multi-step workflows, and sufficient task volume. Product packaging changes over time, so verify current vendor documentation rather than relying on a historical plan name.

  2. Create an IT-controlled automation identity, such as knowledge.automation@YOUR_DOMAIN. Do not use an employee’s personal account as the permanent owner of forms, response sheets, integration credentials, or shared folders.

  3. Create Notion groups for knowledge readers, content owners, reviewers, knowledge administrators, and restricted-content readers. Give general readers view access, owners editing access to the standard knowledge area, and administrators structural access.

  4. Create a standard knowledge teamspace and a separate restricted teamspace. Place the Restricted Knowledge Articles data source in the restricted area. Do not assume that a property value alone prevents unauthorized access.

  5. Create a Notion integration for API operations. Grant only the content read, insert, and update capabilities required by the workflow. Share the specific data sources with the integration. Copy the token once and store it in an approved credential location.

  6. Create or identify the Slack channels #knowledge-review and #knowledge-ops. Restrict automation error messages to the operations channel because they can contain record titles or internal identifiers.

  7. Create a shared Google Drive folder with subfolders for forms, response sheets, controlled templates, migration exports, and test files. Limit access to response sheets because they contain submitter identities and unreviewed content.

  8. Connect Google, Notion, and Slack accounts in Zapier using managed OAuth connections where native actions are used. Restrict Zap editing to the IT administrator, backup administrator, and knowledge coordinator.

  9. Create test users representing a reader, content owner, reviewer, restricted-content reader, and unauthorized employee. Use synthetic procedures rather than real operational or personal information during testing.

  10. Build a separate test set of Notion data sources and test forms. Use distinct names and IDs so test submissions cannot appear in the production knowledge base.

Step 2: Build the Intake

Create two Google Forms: Submit Knowledge and Knowledge Feedback. Restrict both forms to authenticated employees and collect the verified account email. Interface labels may vary, but the required controls are sign-in enforcement, one response identity, and restricted response access.

Submit Knowledge form fields
Question Type and validation Required
Article title Short answer, 10 to 200 characters Yes
Article type Dropdown using the controlled Notion values Yes
Category Dropdown using the controlled category values Yes
Summary Paragraph, 30 to 500 characters Yes
Draft content Paragraph, minimum 40 characters Yes
Why is this needed? Paragraph Yes
Source links Paragraph, one HTTPS link per line No
Equipment or system identifiers Short answer No
Sensitivity Internal or Restricted Yes
Priority Normal, High, or Urgent Yes
Existing article ID Optional KB identifier when proposing a correction No

Use conditional sections to display a warning when Restricted is selected. The warning should tell submitters not to include passwords, API tokens, private keys, regulated personal data, customer confidential information, or other prohibited material.

Do not enable unrestricted file uploads merely for convenience. Ask for links to files already stored in approved Google Drive locations. If file upload is required, place uploaded files in a restricted intake folder and make reviewer-controlled relocation part of the workflow.

The confirmation message should state that submission does not mean publication. It should explain that the employee will receive the Knowledge ID after the automation completes.

The feedback form contains Knowledge ID, Helpful response, Feedback Type, Comment, Urgency, and optional contact preference. The article-specific feedback URL pre-populates Knowledge ID, reducing incorrect article matching.

Google Forms prevents missing required answers. Zapier performs a second validation because source values can still be altered, connectors can return blanks, and legacy responses may not follow current rules.

Step 3: Create the System of Record

  1. Create the Knowledge Articles data source with the exact properties defined earlier. Create all select options before connecting Zapier.

  2. Create a page template with sections for Purpose, Scope, Prerequisites, Procedure or Answer, Verification, Troubleshooting, Related Articles, Controlled Files, Owner, and Change Notes.

  3. Add the Next Review Date formula. Draft records can return blank, while reviewed records return a date.

    if(
      empty(prop("Last Reviewed")),
      "",
      dateAdd(
        prop("Last Reviewed"),
        prop("Review Interval Days"),
        "days"
      )
    )
  4. Create the Knowledge Feedback, Review Log, Automation Errors, and Category Ownership data sources. Add relations from feedback and review events to Knowledge Articles.

  5. Populate Category Ownership with one active row per category. Each row must contain an owner email, reviewer email, review interval, and escalation channel.

  6. Create default views for Published Knowledge, My Drafts, Awaiting My Review, Review Due, Unassigned, Automation Failures, Recently Updated, Retired, and Restricted Knowledge.

  7. Filter reader-facing views to Status = Published. Reviewer and administrator views can include draft and retired statuses.

  8. Use the Knowledge ID as the stable external key. Notion page IDs are also stored by automation, but they are integration identifiers rather than employee-facing references.

  9. Do not sort, delete, or manually rewrite rows in the Google Forms response sheet. It remains a recovery source and should preserve submission order.

  10. Before bulk migration, inventory existing files. Assign each item a disposition of Migrate, Merge, Retire, Retain as Linked File, or Needs Owner. Only reviewed content should become Published.

Step 4: Connect the Tools

Primary integration mappings
Source Destination Trigger and authentication Key mapping Returned value
Google Forms Zapier New form response through managed Google OAuth Answers, timestamp, submitter email, response identifier Trigger event ID
Zapier Notion Code step using a restricted Notion integration token Validated fields to article properties and page body Notion page ID and URL
Zapier Slack Native Slack connection Reviewer email to Slack user, record details to message Channel ID and message timestamp
Notion Zapier New or updated data source item trigger Status, page ID, reviewer, review date, Knowledge ID Zap run identifier
Zapier schedule Notion Daily scheduled trigger and Notion query Published statuses and review dates Line items for due articles
Google Forms feedback Notion and Slack New feedback response Knowledge ID lookup, feedback values, urgency Feedback page ID and alert timestamp

The category lookup should occur before record creation. A Zapier lookup table maps each controlled category to an owner email, reviewer email, review interval, and Slack channel. If no match is found, the Zap must stop normal processing and create a manual-review alert.

For Slack notifications, first find the user by email. Configure the search step so a missing result can follow an alternate path. The primary path sends a direct message. The alternate path sends a message to #knowledge-ops and identifies the email that could not be mapped.

After Slack returns a message timestamp, update the Notion article’s Slack Message ID. If the update fails, the article still exists and the Slack message remains useful, but the run must appear in the error queue.

Step 5: Build the Core Automation

Automation A: Create a Knowledge Draft

  • Trigger: A new response in the Submit Knowledge Google Form.
  • Conditions: Required values are present, category and select values are allowed, source links use HTTPS, and the submitter is an authenticated employee.
  • Actions: Generate the Knowledge ID, map ownership, query for duplicates, create the Notion page, generate the feedback URL, notify the owner and reviewer, and update Slack identifiers.
  • Fields updated: Status, owner, reviewer, dates, review interval, automation status, Notion page ID, feedback URL, and Slack Message ID.
  • Notification: Direct message to the owner and a review notice in the designated channel.
  • Exception: Invalid or unmapped records are marked Manual Review and sent to the knowledge coordinator.

The exact action order is important:

  1. Receive the form event.
  2. Normalize whitespace and select values.
  3. Generate a stable Knowledge ID from the form response identifier and timestamp.
  4. Look up owner, reviewer, review interval, and Slack channel.
  5. Run the Notion creation code, which first checks Knowledge ID.
  6. Receive the page ID, page URL, duplicate flag, and retry count.
  7. Construct the prefilled feedback URL using the Knowledge ID.
  8. Update Feedback URL through a native Notion update action or API request.
  9. Find Slack users by email.
  10. Send the review message.
  11. Write the returned Slack timestamp to Notion.
  12. Send a confirmation containing the Knowledge ID to the submitter.

Automation B: Publish an Approved Article

  • Trigger: A Notion article is created or updated.
  • Conditions: Status equals Approved, required publication fields are complete, and the approval event has not already been processed.
  • Actions: Record the approval event key, set the first Published Date if blank, set Last Reviewed, record Published By, create a Review Log record, and change Status to Published.
  • Fields updated: Published Date, Last Reviewed, Published By, Status, Automation Status, Last Automation Run, and Error Message.
  • Notification: Confirmation to the owner, reviewer, submitter, and knowledge review channel.
  • Exception: Missing owner, reviewer, summary, article body, interval, or sensitivity sends the article to Manual Review instead of publishing it.

Publication must be the final action after validation and review evidence are recorded. If a technical failure occurs after approval, the article remains Approved or is marked Failed rather than being silently treated as published.

Automation C: Capture Article Feedback

  • Trigger: A new response in the Knowledge Feedback form.
  • Conditions: Knowledge ID follows the required format and matches an existing article.
  • Actions: Find the article, create a related Knowledge Feedback record, set initial feedback status to New, and route by urgency.
  • Fields updated: Feedback relation, feedback status, article feedback rollups, and automation audit fields.
  • Notification: Urgent corrections go to the owner, reviewer, and #knowledge-ops. Normal comments appear in the owner’s feedback view.
  • Exception: An unknown Knowledge ID is routed to the coordinator without creating a false article relation.

Automation D: Find Content Due for Review

  • Trigger: A daily scheduled Zap at a defined local time.
  • Conditions: Status is Published or Review Due, Next Review Date is on or before today, and Last Reminder is blank or at least seven days old.
  • Actions: Query all matching Notion pages, paginate results, loop through due articles, find Slack users, send reminders, and update review status and reminder date.
  • Fields updated: Status, Last Reminder, Automation Status, Last Automation Run, Retry Count, and Error Message.
  • Notification: Owner at the due date, reviewer after seven days, and department manager after fourteen days.
  • Exception: Missing owners, invalid review dates, truncated query results, or failed Slack lookups are sent to the manual-review queue.

Automation E: Monitor Technical Failures

  • Trigger: Zapier reports a failed production run or a monitored record remains Processing beyond its expected duration.
  • Conditions: The failure is not an intentional filter stop and has not already been logged.
  • Actions: Create an Automation Errors record, include the workflow and record identifier, and assign a technical owner.
  • Fields updated: Automation Status, Retry Count, Error Message, Last Automation Run, and error resolution status.
  • Notification: Concise alert in #knowledge-ops without tokens or sensitive payload data.
  • Exception: If Notion is unavailable, Zapier retains the run failure and sends its platform alert so the error is not dependent on the failed destination.

Step 6: Add Approvals, Reminders, and Escalations

Content owners can draft and revise an article, but they cannot use automation to approve their own operational guidance unless the governance policy explicitly allows self-approval for a low-risk category. Manufacturing, safety, quality, IT access, and field troubleshooting content require a separate reviewer.

  1. The owner changes Status from Draft or Changes Requested to In Review.
  2. Zapier validates that the summary, page body, category, owner, reviewer, sensitivity, and review interval are present.
  3. Slack notifies the reviewer with the Notion link and expected response date.
  4. The reviewer selects Approved, Changes Requested, Rejected, or Retirement Requested and records notes.
  5. Approved content enters the publication automation. Changes Requested returns ownership to the content owner.
  6. Rejected content requires a rejection reason. The submitter receives the reason and existing article link when duplication caused rejection.
  7. If the reviewer is unavailable, the coordinator reassigns Reviewer Email using the documented category backup. The change is recorded in the Review Log.

Draft reminders run after three business days, while review reminders run after two business days. Because business-day calculations can become complex around holidays, the first implementation uses elapsed-day thresholds and excludes weekends through Zapier date logic. A formal holiday calendar can be added if service-level reporting requires it.

High and urgent items use shorter reminders, but urgency never bypasses approval. An urgent operational correction can cause the current article to be temporarily withdrawn by a human reviewer while the replacement is prepared.

Approval evidence consists of the article ID, previous status, decision, reviewer email, review date, review notes, event key, and automation run identifier. Slack reactions are not treated as formal approval evidence.

Step 7: Add Documents and File Management

Notion page content is the authoritative source for written instructions. Google Drive remains the authoritative source for editable spreadsheets, forms, diagrams, and templates that require native file behavior.

  • Folder structure: Use category folders under a controlled Knowledge Files folder. Separate Active, Under Review, and Retired files only when folder-based access or retention requires it.
  • Naming convention: Prefix controlled files with the Knowledge ID, such as KB-20260714-0042_Torque-Verification-Checklist_v3.xlsx.
  • Permissions: Share folders with managed groups, not individual public links. Restricted files remain in a restricted folder.
  • Version control: Replace uncontrolled duplicate files with one governed file link. Use the file platform’s version history where appropriate.
  • Links: Store file links in Source Links and include them in the Notion page’s Controlled Files section.
  • Replacement: When a file is superseded, update the existing controlled file where version history is useful or add a new file and record the replacement.
  • Retention: Retired files remain available to authorized administrators for the policy-defined period. They are not visible in ordinary published views.
  • Missing documents: The reviewer cannot approve an article when a referenced required file is inaccessible.
  • Duplicate documents: The knowledge coordinator selects one authoritative file and records the disposition of duplicates during migration.

Zapier should validate that source links begin with https://. It cannot guarantee that every recipient has permission, so user acceptance testing must include opening each link as a standard reader.

Large file uploads are not passed through multiple automation steps. The Form collects a controlled Drive link, reducing task size, timeouts, and accidental replication. A failed upload remains visible in the intake folder and is handled before publication.

Step 8: Add Reporting and Operational Views

Operational views and filters
View Filter Owner and use
New Drafts Status is Draft and Submission Date is within 30 days Knowledge coordinator checks intake progress.
Awaiting My Action Owner or Reviewer matches the current user and status requires action Owners and reviewers manage assigned work.
Overdue Reviews Status is Review Due, sorted by Next Review Date Operations managers monitor stale content.
Incomplete Records Required metadata is blank Coordinator corrects migration and automation gaps.
Urgent Feedback Feedback urgency is Urgent and status is not Resolved Owner and reviewer assess possible incorrect guidance.
Rejected Submissions Status is Rejected Coordinator identifies duplication or scope problems.
Upcoming Reviews Next Review Date falls within 30 days Owners plan workload before content becomes overdue.
Recently Published Published Date is within 30 days Employees and managers see new guidance.
Automation Failures Automation Status is Failed or Manual Review IT administrator performs recovery.
Unassigned Content Owner Email or Reviewer Email is blank Coordinator assigns accountable owners.
Retired Content Status is Retired Administrators manage replacement and retention.

The Notion dashboard shows counts by status, category, owner, sensitivity, and review age. Formula properties can calculate days until review and days overdue. Reporting owners should avoid treating Notion’s current date formulas as formal historical metrics because values change over time.

For historical reporting, a weekly scheduled Zap can write status counts to a metrics data source. That preserves snapshots for volume, overdue rate, average review duration, feedback closure, and automation failures. The knowledge coordinator owns business reporting, while IT owns automation reliability reporting.

Alert thresholds for the representative implementation are: any urgent correction, more than five articles without owners, any approval stuck for more than one hour, more than ten overdue reviews, or more than three automation failures in a day. These are operating assumptions rather than universal targets.

Step 9: Add Security and Governance Controls

  • Least privilege: Readers receive view access. Owners receive only the editing access necessary for their content area. Structural changes remain limited to administrators.
  • Restricted content: Sensitive runbooks use a separate restricted data source and teamspace. A select property is not an access control.
  • Shared links: Disable public sharing for internal knowledge and controlled files unless a separately approved publishing process exists.
  • Credentials: Store Google, Slack, Notion, and optional AI credentials in managed connections or approved secret storage. Never place tokens in page content, Slack messages, or logs.
  • Automation editing: Limit Zap access because editors may be able to view mappings, test records, and credential-related configuration.
  • Activity evidence: Retain Zap run identifiers, Notion review logs, Form response timestamps, and record status changes according to policy.
  • Former employees: Remove workspace, Slack, Google, Notion, and Zapier access through the normal offboarding process. Reassign owned articles before disabling the account.
  • Backups: Perform scheduled exports of critical knowledge and metadata. Test that exported content can be located and restored.
  • Privacy: Avoid collecting personal information not required for knowledge governance. Restrict response sheets and error payloads.
  • AI restrictions: Do not send restricted articles, credentials, private keys, regulated personal data, or customer confidential content to an unapproved AI service.
  • Human control: Publication, operational correctness, policy exceptions, restricted access, and retirement remain human decisions.

The governance policy should identify the authoritative system for each content type. Notion is authoritative for published written instructions. Google Drive is authoritative for linked controlled files. Slack is a notification and discussion channel, not a publication system.

Step 10: Deploy and Test

  1. Build all data sources, forms, Zaps, lookup values, and Slack messages in the test environment.

  2. Create synthetic records for every category, status, sensitivity level, reminder level, and failure path.

  3. Run technical testing with the IT administrator and knowledge coordinator. Confirm API pagination, duplicate prevention, retries, permissions, and error logging.

  4. Run user acceptance testing with two owners, two reviewers, and several readers. Ask them to submit, edit, review, search, provide feedback, and open controlled files.

  5. Pilot one content category for two weeks. Do not migrate all 180 existing documents before the workflow and naming conventions have been validated.

  6. Export the production configuration and document all data source IDs, Form owners, Slack channels, connection owners, lookup values, and recovery procedures.

  7. Activate production Zaps in sequence: intake, feedback, publication, scheduled reviews, and error monitoring.

  8. Migrate existing content in batches. Every migrated article begins as Draft or In Review unless there is current approval evidence.

  9. Communicate the launch with links to the published knowledge view, submission form, feedback form, and support contact.

  10. Monitor every run during the pilot week. Maintain a rollback option by retaining the original files read-only until migration validation is complete.

Code and Configuration

The standard status changes, Slack messages, filters, and Notion updates use native Zapier actions. Code is used for two tasks that benefit from stronger control: idempotent Notion page creation and paginated scheduled review queries.

Notion API Authentication and Request Pattern

Create a Notion integration, grant the required content capabilities, and share the relevant data sources with it. The scripts use bearer authentication and the Notion API version represented by 2025-09-03. Verify the current supported API version before deployment.

Authorization: Bearer YOUR_NOTION_TOKEN
Content-Type: application/json
Notion-Version: 2025-09-03

Create page:
POST https://api.notion.com/v1/pages

Query data source:
POST https://api.notion.com/v1/data_sources/YOUR_DATA_SOURCE_ID/query

Never print the token or include it in an error message. The integration must be shared with the Knowledge Articles data source or the API will return a not-found or authorization response even when the identifier is correct.

Idempotent Knowledge Draft Creation

Place the following code in a Python Code by Zapier action after validation and category assignment. Map the input fields listed in the comments. The script validates values, checks for an existing Knowledge ID, creates the Notion page and page body, retries temporary failures, and returns the page ID and URL.

import json
import re
import time
from datetime import datetime, timezone
from urllib.error import HTTPError, URLError
from urllib.parse import quote
from urllib.request import Request, urlopen

BASE_URL = "https://api.notion.com/v1"
DEFAULT_NOTION_VERSION = "2025-09-03"
RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}

ALLOWED_ARTICLE_TYPES = {
    "Procedure",
    "Troubleshooting",
    "Answer",
    "Policy",
    "Template",
    "Checklist",
}

ALLOWED_CATEGORIES = {
    "Manufacturing",
    "Field Service",
    "Quality",
    "IT",
    "Safety",
    "General Operations",
}

ALLOWED_SENSITIVITY = {"Internal", "Restricted"}
ALLOWED_PRIORITY = {"Normal", "High", "Urgent"}
ALLOWED_REVIEW_INTERVALS = {90, 180, 365}

EMAIL_PATTERN = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
KNOWLEDGE_ID_PATTERN = re.compile(r"^KB-\d{8}-\d{4,8}$")


class NotionApiError(Exception):
    def __init__(self, status, message, retry_after=None):
        super().__init__(message)
        self.status = status
        self.retry_after = retry_after


def required_input(name):
    value = str(input_data.get(name, "")).strip()
    if not value:
        raise ValueError("Missing required input: {}".format(name))
    return value


def optional_input(name):
    return str(input_data.get(name, "") or "").strip()


def validate_email(value, field_name):
    if not EMAIL_PATTERN.match(value):
        raise ValueError("Invalid email in {}".format(field_name))


def normalize_iso_datetime(value):
    cleaned = value.strip().replace("Z", "+00:00")
    try:
        parsed = datetime.fromisoformat(cleaned)
    except ValueError as exc:
        raise ValueError("submitted_at must be an ISO 8601 date or timestamp") from exc

    if parsed.tzinfo is None:
        parsed = parsed.replace(tzinfo=timezone.utc)

    return parsed.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")


def rich_text(value, chunk_size=1900):
    if not value:
        return []

    return [
        {
            "type": "text",
            "text": {
                "content": value[index:index + chunk_size]
            },
        }
        for index in range(0, len(value), chunk_size)
    ]


def paragraph_blocks(value):
    blocks = []

    for line in value.splitlines():
        text = line.strip()
        if not text:
            continue

        for index in range(0, len(text), 1900):
            blocks.append(
                {
                    "object": "block",
                    "type": "paragraph",
                    "paragraph": {
                        "rich_text": rich_text(text[index:index + 1900])
                    },
                }
            )

    if not blocks:
        raise ValueError("draft_body did not contain usable text")

    if len(blocks) > 90:
        raise ValueError("draft_body creates too many Notion blocks")

    return blocks


def api_call_once(method, path, token, notion_version, payload=None):
    url = BASE_URL + path
    data = None if payload is None else json.dumps(payload).encode("utf-8")

    request = Request(
        url=url,
        data=data,
        method=method,
        headers={
            "Authorization": "Bearer {}".format(token),
            "Content-Type": "application/json",
            "Notion-Version": notion_version,
        },
    )

    try:
        with urlopen(request, timeout=25) as response:
            response_body = response.read().decode("utf-8")
            return json.loads(response_body) if response_body else {}
    except HTTPError as exc:
        body = exc.read().decode("utf-8", errors="replace")
        retry_after = exc.headers.get("Retry-After")
        safe_message = "Notion API returned HTTP {}: {}".format(
            exc.code,
            body[:800],
        )
        raise NotionApiError(exc.code, safe_message, retry_after) from exc
    except URLError as exc:
        raise NotionApiError(
            0,
            "Network error while calling Notion: {}".format(str(exc.reason)[:300]),
        ) from exc


def api_call_with_retry(
    method,
    path,
    token,
    notion_version,
    payload=None,
    max_attempts=4,
):
    last_error = None

    for attempt in range(1, max_attempts + 1):
        try:
            return api_call_once(
                method,
                path,
                token,
                notion_version,
                payload,
            )
        except NotionApiError as exc:
            last_error = exc

            if exc.status not in RETRYABLE_STATUS_CODES and exc.status != 0:
                raise

            if attempt == max_attempts:
                break

            try:
                wait_seconds = float(exc.retry_after) if exc.retry_after else 2 ** (attempt - 1)
            except ValueError:
                wait_seconds = 2 ** (attempt - 1)

            wait_seconds = min(max(wait_seconds, 1), 12)
            print("Temporary Notion error. Retry attempt {} after {} seconds.".format(
                attempt,
                wait_seconds,
            ))
            time.sleep(wait_seconds)

    raise last_error


def validate_https_links(source_links):
    if not source_links:
        return

    for line in source_links.splitlines():
        link = line.strip()
        if link and not link.startswith("https://"):
            raise ValueError("Every source link must begin with https://")


def find_existing_page(
    data_source_id,
    knowledge_id,
    token,
    notion_version,
):
    path = "/data_sources/{}/query".format(
        quote(data_source_id, safe="")
    )

    payload = {
        "page_size": 1,
        "filter": {
            "property": "Knowledge ID",
            "rich_text": {
                "equals": knowledge_id
            },
        },
    }

    response = api_call_with_retry(
        "POST",
        path,
        token,
        notion_version,
        payload,
    )

    results = response.get("results", [])
    return results[0] if results else None


def build_page_payload(values, retry_count):
    properties = {
        "Name": {
            "title": rich_text(values["title"])
        },
        "Knowledge ID": {
            "rich_text": rich_text(values["knowledge_id"])
        },
        "Article Type": {
            "select": {"name": values["article_type"]}
        },
        "Category": {
            "select": {"name": values["category"]}
        },
        "Summary": {
            "rich_text": rich_text(values["summary"])
        },
        "Status": {
            "select": {"name": "Draft"}
        },
        "Priority": {
            "select": {"name": values["priority"]}
        },
        "Submitter Email": {
            "email": values["submitter_email"]
        },
        "Owner Email": {
            "email": values["owner_email"]
        },
        "Reviewer Email": {
            "email": values["reviewer_email"]
        },
        "Sensitivity": {
            "select": {"name": values["sensitivity"]}
        },
        "Submission Date": {
            "date": {"start": values["submitted_at"]}
        },
        "Review Interval Days": {
            "number": values["review_interval_days"]
        },
        "Source Links": {
            "rich_text": rich_text(values["source_links"])
        },
        "Automation Status": {
            "select": {"name": "OK"}
        },
        "Last Automation Run": {
            "date": {"start": values["run_timestamp"]}
        },
        "Retry Count": {
            "number": retry_count
        },
        "Error Message": {
            "rich_text": []
        },
    }

    children = [
        {
            "object": "block",
            "type": "heading_2",
            "heading_2": {
                "rich_text": rich_text("Submitted draft")
            },
        },
        {
            "object": "block",
            "type": "paragraph",
            "paragraph": {
                "rich_text": rich_text(values["summary"])
            },
        },
    ]

    children.extend(paragraph_blocks(values["draft_body"]))

    if values["business_need"]:
        children.append(
            {
                "object": "block",
                "type": "heading_2",
                "heading_2": {
                    "rich_text": rich_text("Business need")
                },
            }
        )
        children.extend(paragraph_blocks(values["business_need"]))

    return {
        "parent": {
            "type": "data_source_id",
            "data_source_id": values["data_source_id"],
        },
        "properties": properties,
        "children": children,
    }


def prepare_values():
    notion_token = required_input("notion_token")
    data_source_id = required_input("data_source_id")
    knowledge_id = required_input("knowledge_id")
    title = required_input("title")
    article_type = required_input("article_type")
    category = required_input("category")
    summary = required_input("summary")
    draft_body = required_input("draft_body")
    business_need = required_input("business_need")
    submitter_email = required_input("submitter_email")
    owner_email = required_input("owner_email")
    reviewer_email = required_input("reviewer_email")
    sensitivity = required_input("sensitivity")
    priority = required_input("priority")
    source_links = optional_input("source_links")
    submitted_at = normalize_iso_datetime(required_input("submitted_at"))
    notion_version = optional_input("notion_version") or DEFAULT_NOTION_VERSION

    try:
        review_interval_days = int(required_input("review_interval_days"))
    except ValueError as exc:
        raise ValueError("review_interval_days must be an integer") from exc

    if not KNOWLEDGE_ID_PATTERN.match(knowledge_id):
        raise ValueError("knowledge_id must match KB-YYYYMMDD-NNNN")

    if not 10 <= len(title) <= 200:
        raise ValueError("title must contain 10 to 200 characters")

    if not 30 <= len(summary) <= 500:
        raise ValueError("summary must contain 30 to 500 characters")

    if not 40 <= len(draft_body) <= 20000:
        raise ValueError("draft_body must contain 40 to 20000 characters")

    if article_type not in ALLOWED_ARTICLE_TYPES:
        raise ValueError("Unsupported article_type")

    if category not in ALLOWED_CATEGORIES:
        raise ValueError("Unsupported category")

    if sensitivity not in ALLOWED_SENSITIVITY:
        raise ValueError("Unsupported sensitivity")

    if priority not in ALLOWED_PRIORITY:
        raise ValueError("Unsupported priority")

    if review_interval_days not in ALLOWED_REVIEW_INTERVALS:
        raise ValueError("Unsupported review_interval_days")

    validate_email(submitter_email, "submitter_email")
    validate_email(owner_email, "owner_email")
    validate_email(reviewer_email, "reviewer_email")
    validate_https_links(source_links)

    return {
        "notion_token": notion_token,
        "notion_version": notion_version,
        "data_source_id": data_source_id,
        "knowledge_id": knowledge_id,
        "title": title,
        "article_type": article_type,
        "category": category,
        "summary": summary,
        "draft_body": draft_body,
        "business_need": business_need,
        "submitter_email": submitter_email,
        "owner_email": owner_email,
        "reviewer_email": reviewer_email,
        "sensitivity": sensitivity,
        "priority": priority,
        "source_links": source_links,
        "submitted_at": submitted_at,
        "review_interval_days": review_interval_days,
        "run_timestamp": datetime.now(timezone.utc).isoformat().replace(
            "+00:00",
            "Z",
        ),
    }


def main():
    values = prepare_values()

    existing = find_existing_page(
        values["data_source_id"],
        values["knowledge_id"],
        values["notion_token"],
        values["notion_version"],
    )

    if existing:
        print("Existing knowledge record found: {}".format(
            values["knowledge_id"]
        ))
        return {
            "knowledge_id": values["knowledge_id"],
            "page_id": existing["id"],
            "page_url": existing.get("url", ""),
            "duplicate": True,
            "retry_count": 0,
            "status": "existing",
        }

    last_error = None

    for attempt in range(1, 5):
        existing = find_existing_page(
            values["data_source_id"],
            values["knowledge_id"],
            values["notion_token"],
            values["notion_version"],
        )

        if existing:
            return {
                "knowledge_id": values["knowledge_id"],
                "page_id": existing["id"],
                "page_url": existing.get("url", ""),
                "duplicate": True,
                "retry_count": attempt - 1,
                "status": "existing_after_retry",
            }

        payload = build_page_payload(values, attempt - 1)

        try:
            created = api_call_once(
                "POST",
                "/pages",
                values["notion_token"],
                values["notion_version"],
                payload,
            )

            print("Created knowledge record: {}".format(
                values["knowledge_id"]
            ))

            return {
                "knowledge_id": values["knowledge_id"],
                "page_id": created["id"],
                "page_url": created.get("url", ""),
                "duplicate": False,
                "retry_count": attempt - 1,
                "status": "created",
            }
        except NotionApiError as exc:
            last_error = exc

            if exc.status not in RETRYABLE_STATUS_CODES and exc.status != 0:
                raise

            if attempt == 4:
                break

            try:
                wait_seconds = float(exc.retry_after) if exc.retry_after else 2 ** (attempt - 1)
            except ValueError:
                wait_seconds = 2 ** (attempt - 1)

            time.sleep(min(max(wait_seconds, 1), 12))

    raise last_error


output = main()

Map notion_token and data_source_id to the correct standard or restricted data source based on the sensitivity branch. The token is a credential and should only be visible to authorized Zap editors.

Test the script with a valid synthetic submission. The expected output includes knowledge_id, page_id, page_url, duplicate, retry_count, and status. Run the same input again and confirm that the script returns the existing page rather than creating another one.

Common errors include a data source that was not shared with the integration, property names that do not match exactly, missing select options, an obsolete API version, invalid email values, and malformed data source IDs. Inspect the Zap run output and Notion API response, but redact credentials before sharing logs.

Paginated Scheduled Review Query

Place the next script in the daily scheduled Zap. It queries all Published and Review Due records, handles pagination, evaluates dates, suppresses reminders sent within the configured gap, and returns line-item arrays for Looping by Zapier.

import json
import time
from datetime import date, datetime
from urllib.error import HTTPError, URLError
from urllib.parse import quote
from urllib.request import Request, urlopen

BASE_URL = "https://api.notion.com/v1"
DEFAULT_NOTION_VERSION = "2025-09-03"
RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}


class NotionApiError(Exception):
    def __init__(self, status, message, retry_after=None):
        super().__init__(message)
        self.status = status
        self.retry_after = retry_after


def required_input(name):
    value = str(input_data.get(name, "")).strip()
    if not value:
        raise ValueError("Missing required input: {}".format(name))
    return value


def optional_input(name):
    return str(input_data.get(name, "") or "").strip()


def api_call_once(method, path, token, notion_version, payload=None):
    data = None if payload is None else json.dumps(payload).encode("utf-8")
    request = Request(
        BASE_URL + path,
        data=data,
        method=method,
        headers={
            "Authorization": "Bearer {}".format(token),
            "Content-Type": "application/json",
            "Notion-Version": notion_version,
        },
    )

    try:
        with urlopen(request, timeout=25) as response:
            body = response.read().decode("utf-8")
            return json.loads(body) if body else {}
    except HTTPError as exc:
        body = exc.read().decode("utf-8", errors="replace")
        raise NotionApiError(
            exc.code,
            "Notion API returned HTTP {}: {}".format(exc.code, body[:800]),
            exc.headers.get("Retry-After"),
        ) from exc
    except URLError as exc:
        raise NotionApiError(
            0,
            "Network error while calling Notion: {}".format(str(exc.reason)[:300]),
        ) from exc


def api_call_with_retry(
    method,
    path,
    token,
    notion_version,
    payload=None,
    max_attempts=4,
):
    last_error = None

    for attempt in range(1, max_attempts + 1):
        try:
            return api_call_once(
                method,
                path,
                token,
                notion_version,
                payload,
            )
        except NotionApiError as exc:
            last_error = exc

            if exc.status not in RETRYABLE_STATUS_CODES and exc.status != 0:
                raise

            if attempt == max_attempts:
                break

            try:
                wait_seconds = float(exc.retry_after) if exc.retry_after else 2 ** (attempt - 1)
            except ValueError:
                wait_seconds = 2 ** (attempt - 1)

            time.sleep(min(max(wait_seconds, 1), 12))

    raise last_error


def read_title(prop):
    return "".join(
        item.get("plain_text", "")
        for item in prop.get("title", [])
    ).strip()


def read_rich_text(prop):
    return "".join(
        item.get("plain_text", "")
        for item in prop.get("rich_text", [])
    ).strip()


def read_email(prop):
    return str(prop.get("email") or "").strip()


def read_select(prop):
    value = prop.get("select")
    return str(value.get("name", "")).strip() if value else ""


def read_date(prop):
    value = prop.get("date")
    return str(value.get("start", "")).strip() if value else ""


def read_formula_date(prop):
    formula = prop.get("formula") or {}
    value = formula.get("date")
    return str(value.get("start", "")).strip() if value else ""


def parse_date(value, field_name):
    try:
        return date.fromisoformat(value[:10])
    except (TypeError, ValueError) as exc:
        raise ValueError(
            "{} is not a valid ISO date: {}".format(field_name, value)
        ) from exc


def query_all_pages(data_source_id, token, notion_version):
    path = "/data_sources/{}/query".format(
        quote(data_source_id, safe="")
    )

    pages = []
    start_cursor = None

    while True:
        payload = {
            "page_size": 100,
            "filter": {
                "or": [
                    {
                        "property": "Status",
                        "select": {"equals": "Published"},
                    },
                    {
                        "property": "Status",
                        "select": {"equals": "Review Due"},
                    },
                ]
            },
        }

        if start_cursor:
            payload["start_cursor"] = start_cursor

        response = api_call_with_retry(
            "POST",
            path,
            token,
            notion_version,
            payload,
        )

        pages.extend(response.get("results", []))

        if not response.get("has_more"):
            break

        start_cursor = response.get("next_cursor")
        if not start_cursor:
            raise RuntimeError(
                "Notion indicated more results but returned no cursor"
            )

    return pages


def main():
    token = required_input("notion_token")
    data_source_id = required_input("data_source_id")
    notion_version = optional_input("notion_version") or DEFAULT_NOTION_VERSION
    fallback_reviewer = required_input("fallback_reviewer_email")

    today_value = optional_input("today")
    today = parse_date(
        today_value if today_value else date.today().isoformat(),
        "today",
    )

    try:
        reminder_gap_days = int(
            optional_input("reminder_gap_days") or "7"
        )
        max_items = int(optional_input("max_items") or "100")
    except ValueError as exc:
        raise ValueError(
            "reminder_gap_days and max_items must be integers"
        ) from exc

    if reminder_gap_days < 1 or reminder_gap_days > 30:
        raise ValueError("reminder_gap_days must be between 1 and 30")

    if max_items < 1 or max_items > 200:
        raise ValueError("max_items must be between 1 and 200")

    pages = query_all_pages(
        data_source_id,
        token,
        notion_version,
    )

    due_items = []
    configuration_errors = []

    for page in pages:
        props = page.get("properties", {})

        title = read_title(props.get("Name", {}))
        knowledge_id = read_rich_text(
            props.get("Knowledge ID", {})
        )
        owner_email = read_email(props.get("Owner Email", {}))
        reviewer_email = read_email(
            props.get("Reviewer Email", {})
        ) or fallback_reviewer
        status = read_select(props.get("Status", {}))
        due_value = read_formula_date(
            props.get("Next Review Date", {})
        )
        last_reminder_value = read_date(
            props.get("Last Reminder", {})
        )

        missing = []
        if not title:
            missing.append("Name")
        if not knowledge_id:
            missing.append("Knowledge ID")
        if not owner_email:
            missing.append("Owner Email")
        if not due_value:
            missing.append("Next Review Date")

        if missing:
            configuration_errors.append(
                "{} missing {}".format(
                    knowledge_id or page.get("id", "unknown"),
                    ", ".join(missing),
                )
            )
            continue

        try:
            due_date = parse_date(
                due_value,
                "Next Review Date",
            )
        except ValueError as exc:
            configuration_errors.append(str(exc))
            continue

        if due_date > today:
            continue

        if last_reminder_value:
            try:
                last_reminder = parse_date(
                    last_reminder_value,
                    "Last Reminder",
                )
                if (today - last_reminder).days < reminder_gap_days:
                    continue
            except ValueError as exc:
                configuration_errors.append(str(exc))
                continue

        days_overdue = (today - due_date).days

        if days_overdue >= 14:
            reminder_level = "manager_escalation"
        elif days_overdue >= 7:
            reminder_level = "reviewer_escalation"
        else:
            reminder_level = "owner_reminder"

        due_items.append(
            {
                "page_id": page["id"],
                "page_url": page.get("url", ""),
                "knowledge_id": knowledge_id,
                "title": title,
                "owner_email": owner_email,
                "reviewer_email": reviewer_email,
                "status": status,
                "due_date": due_date.isoformat(),
                "days_overdue": days_overdue,
                "reminder_level": reminder_level,
            }
        )

    due_items.sort(
        key=lambda item: item["days_overdue"],
        reverse=True,
    )

    truncated = len(due_items) > max_items
    due_items = due_items[:max_items]

    print(
        "Reviewed {} pages and found {} reminder items.".format(
            len(pages),
            len(due_items),
        )
    )

    return {
        "due_count": len(due_items),
        "page_id": [item["page_id"] for item in due_items],
        "page_url": [item["page_url"] for item in due_items],
        "knowledge_id": [
            item["knowledge_id"] for item in due_items
        ],
        "title": [item["title"] for item in due_items],
        "owner_email": [
            item["owner_email"] for item in due_items
        ],
        "reviewer_email": [
            item["reviewer_email"] for item in due_items
        ],
        "due_date": [item["due_date"] for item in due_items],
        "days_overdue": [
            item["days_overdue"] for item in due_items
        ],
        "reminder_level": [
            item["reminder_level"] for item in due_items
        ],
        "run_date": today.isoformat(),
        "configuration_error_count": len(configuration_errors),
        "configuration_errors": "\n".join(
            configuration_errors[:25]
        ),
        "truncated": truncated,
        "total_pages_examined": len(pages),
    }


output = main()

After the code step, add a filter requiring due_count to be greater than zero. Pass all line-item arrays to Looping by Zapier. Within each loop:

  1. Find the owner in Slack by email.
  2. Use paths based on reminder_level.
  3. Send the appropriate owner, reviewer, or manager notification.
  4. Update the Notion page using the returned page_id.
  5. Set Status to Review Due, Last Reminder to run_date, Automation Status to OK, and Last Automation Run to the current timestamp.
  6. If the Slack user is not found, notify #knowledge-ops and still mark the record Manual Review rather than pretending delivery succeeded.

Add a parallel path for configuration_error_count greater than zero or truncated equal to true. That path sends an administrator alert. When results are truncated, successfully processed items receive Last Reminder dates, allowing the next daily run to select remaining records.

Slack Message Configuration

Knowledge review required

ID: {{knowledge_id}}
Title: {{title}}
Due date: {{due_date}}
Days overdue: {{days_overdue}}
Owner: {{owner_email}}
Reviewer: {{reviewer_email}}

Open article: {{page_url}}

Review the content, verify linked files, record review notes,
and move the article to In Review when ready.

Keep messages concise and avoid copying the complete article into Slack. The Notion page remains authoritative. Use the Slack message timestamp returned by the action when a thread or audit reference is needed.

Failure Handling and Operational Reliability

Failure handling and recovery
Failure Automated response Manual recovery Owner
Missing required form data Form blocks submission or Zap validation stops creation. Correct the response or submit a complete replacement. Submitter and coordinator
Duplicate form event Knowledge ID query returns the existing Notion page. Confirm the existing record and close the duplicate run. IT administrator
Duplicate content with a different ID Reviewer sees existing article reference or optional AI suggestion. Merge, reject, or relate the records. Reviewer
Invalid select value Validation raises an error before record creation. Update the controlled mapping and replay the run. Coordinator
Partial completion Automation Status remains Processing or Failed. Resume from the last confirmed external ID rather than recreating the article. IT administrator
Notion authentication failure API returns an authorization error and retries stop. Reconnect or rotate the integration token, confirm sharing, and replay. IT administrator
Notion temporary failure or rate limit Script waits and retries temporary responses using Retry-After when supplied. Replay failed items after service recovery. IT administrator
Unavailable approver Reminder is escalated and the item remains pending. Coordinator assigns the documented backup reviewer. Knowledge coordinator
Failed file link Reviewer cannot approve required linked material. Correct permissions or replace the file link. Content owner
Invalid email address Validation stops creation or Slack lookup follows fallback path. Correct Category Ownership and rerun notification. Coordinator
Slack notification failure Zap run fails before Last Reminder is updated. Resolve Slack connection and replay. The next daily run can also select the item. IT administrator
Scheduled query truncation Administrator alert is sent and processed records receive reminder dates. Increase capacity cautiously or run an additional controlled batch. IT administrator
Malformed review date Record appears in configuration errors instead of the reminder loop. Correct Last Reviewed or Review Interval Days. Coordinator
Automation error logging failure Zapier platform alert remains available independently of Notion. Create the error record manually after service recovery. IT administrator

Idempotency is based on Knowledge ID for article creation and Feedback ID for feedback creation. Review events use an event key composed from the article ID, decision, reviewer, and source event timestamp. Automation must search for that key before adding a second Review Log record.

The Google Forms response sheet acts as a reconciliation source. A daily or weekly control compares successful form responses with Notion Knowledge IDs. Any response without a matching article appears in a recovery list.

Manual recovery follows a fixed order: identify the source response, search Notion by Knowledge ID, inspect the latest Zap run, confirm whether Slack or Notion actions completed, correct the underlying problem, and replay only the missing action. Staff should not delete a partially created article merely to make a Zap run appear clean.

A Complete Example

An operations supervisor submits a proposed troubleshooting article titled Reset Procedure for Line 2 Torque Controller. The submission is authenticated through Google Forms and includes the following values:

  • Article type: Troubleshooting
  • Category: Manufacturing
  • Summary: Steps for placing the controller in a safe state, clearing a recoverable fault, and verifying normal operation.
  • Sensitivity: Internal
  • Priority: High
  • Source links: A controlled wiring diagram and inspection checklist in Google Drive
  • Submission date: July 14, 2026

Zapier receives the response and generates KB-20260714-0042. The category lookup assigns the manufacturing systems owner, the manufacturing manager as reviewer, and a 90-day interval.

The creation script queries Notion for the Knowledge ID. No record exists, so it creates the page, writes the submitted draft into the page body, and returns PAGE_ID_EXAMPLE and the Notion page URL. The identifier shown here is illustrative rather than a real platform identifier.

Zapier constructs the article-specific feedback URL, sends a Slack message to the owner, posts a review notice in #knowledge-review, and stores the Slack message timestamp on the article.

The owner checks the procedure against the equipment manual, adds a lockout prerequisite, confirms the linked diagram permissions, and changes Status to In Review. The reviewer identifies one missing verification step and changes Status to Changes Requested.

After correction, the owner resubmits it. The reviewer approves the article on July 15, 2026. Zapier records the review event, sets Published Date and Last Reviewed, changes Status to Published, and calculates the next review date as October 13, 2026.

In August, a technician submits normal-priority feedback stating that the controller display uses a different label after a firmware update. Zapier creates a feedback record related to KB-20260714-0042 and assigns it to the owner. The owner verifies the firmware difference and updates the article through the standard review process.

If the October review date arrives without a completed review, the scheduled Zap selects the article. It sends the owner a reminder and changes Status to Review Due. At seven days overdue, the reviewer is included. At fourteen days, the department manager receives the escalation. Publication remains a human decision throughout.

Implementation Cost

All amounts below are representative planning assumptions, not vendor quotes or verified client costs. Current licensing, implementation rates, taxes, usage limits, and regional pricing must be confirmed before purchase.

Representative one-time implementation costs
Item Assumption Estimated cost
Requirements and content audit 20 internal hours at $55 per hour $1,100
Content cleanup and migration preparation 36 internal hours at $50 per hour $1,800
User acceptance testing 16 hours at $50 per hour $800
Training 8 hours at $45 per hour $360
Documentation 10 hours at $55 per hour $550
Optional professional design and automation build 32 hours at an assumed $165 per hour $5,280
Total with professional implementation Representative combined assumption $9,890
Representative recurring monthly costs
Item Assumption Estimated monthly cost
Notion capacity and contributor access Planning allowance, verify current licensing $100
Zapier task and workflow capacity Planning allowance for production volume $85
Google Workspace Existing subscription, no incremental license assumed $0 incremental
Slack Existing subscription, no incremental license assumed $0 incremental
Internal maintenance labour 3 hours at $55 per hour $165
Optional professional maintenance Up to 2 hours at $165 per hour when required Up to $330
Optional AI usage Excluded from the core implementation Estimated separately

An existing subscription may have no incremental license charge, but it still requires administration, permission review, testing, and maintenance. Those activities are included in the labour assumptions rather than treated as cost-free.

Estimated Time and Cost Savings

The representative company processes 65 knowledge workflow events per month: 25 submissions, 28 scheduled review events, and 12 feedback items. The calculation uses an average across those event types.

Savings assumptions
Assumption Value
Monthly workflow volume 65 events
Current average handling time 24 minutes per event
New average handling time 8 minutes per event
Exception rate 10 percent
Exception handling time 12 minutes
Monthly maintenance time 3 hours
Loaded hourly labour cost $55
Recurring software cost $185 per month
One-time implementation cost $9,890

Current monthly labour hours: Monthly volume × current minutes per record ÷ 60

Current calculation: 65 × 24 ÷ 60 = 26.00 hours

New monthly labour hours: Monthly volume × new minutes per record ÷ 60, plus exception handling and maintenance

New calculation: 65 × 8 ÷ 60 + 65 × 10% × 12 ÷ 60 + 3 = 12.97 hours

Monthly hours recovered: Current monthly labour hours minus new monthly labour hours

Hours recovered calculation: 26.00 – 12.97 = 13.03 hours

Estimated monthly labour value: Monthly hours recovered × loaded hourly labour cost

Labour value calculation: 13.03 × $55 = $716.65

Net estimated monthly value: Monthly labour value minus recurring tool costs

Net value calculation: $716.65 – $185 = $531.65

Estimated payback period: One-time implementation cost ÷ net estimated monthly value

Payback calculation: $9,890 ÷ $531.65 = approximately 18.6 months

Recovered hours do not automatically reduce payroll. They can represent additional capacity, faster responses, reduced overtime, less administrative follow-up, higher content volume, or lower dependency on experienced employees.

Non-financial benefits include clearer ownership, fewer incomplete submissions, more consistent review evidence, easier search, visible publication status, better handling of employee feedback, and a defensible retirement process.

Readers should replace the workflow volume, handling times, exception rate, wage assumptions, subscription costs, migration effort, implementation rate, and maintenance time with their own figures.

Adding AI to the Automation

AI is optional and should be added only after deterministic intake, ownership, publication, permissions, and review controls operate reliably.

Useful AI applications include suggesting categories, drafting a short summary, identifying potentially missing prerequisites, extracting equipment identifiers, proposing search aliases, and suggesting possible duplicate search terms.

AI should not replace required fields, exact Knowledge ID matching, category lookup tables, review-date formulas, access permissions, publication rules, or retirement approvals. Those functions are more dependable when implemented through normal validation and workflow logic.

The core automation provides structured records, assignment, reminders, status control, feedback routing, and audit evidence. AI adds value only when interpreting unstructured draft content.

The recommended enhancement analyzes a new internal draft and proposes a category, summary, search terms, and missing-information checklist. It does not publish, reject, or change ownership.

  • Trigger: A valid internal-sensitivity draft is created in Notion.
  • AI input: Title, submitted summary, draft body, article type, category choices, and non-sensitive equipment identifiers.
  • System instruction: Analyze only supplied content, use the allowed categories, identify uncertainty, and never claim that an operational procedure is safe or approved.
  • Expected output: Structured JSON containing suggestions and confidence.
  • Validation: Confirm JSON structure, allowed category, string lengths, and confidence range.
  • Record update: Write results to AI Suggested Summary, AI Suggested Category, AI Missing Information, and AI Confidence fields.
  • Human review: The content owner accepts, edits, or rejects each suggestion.
  • Low confidence: Confidence below 0.75 is marked for manual categorization.
  • Prohibited data: Restricted articles, credentials, personal data, customer confidential data, private keys, and security secrets.
  • Failure behavior: Leave AI fields blank, record the technical failure, and continue the normal human workflow.

Use the following reusable system instruction:

You assist with internal knowledge-base preparation.

Analyze only the supplied draft. Do not invent procedures, safety steps,
technical facts, approvals, or source references. Do not determine whether
the article should be published. Select a category only from the allowed
list. Identify missing information as questions for a human content owner.

Return only JSON matching the supplied schema. If the content is ambiguous,
lower confidence and explain the uncertainty in reasoning_summary. Do not
include hidden reasoning or chain-of-thought. Use concise, reviewable output.

Use this user prompt template:

Allowed categories:
Manufacturing
Field Service
Quality
IT
Safety
General Operations

Article type: {{article_type}}
Submitted category: {{submitted_category}}
Title: {{title}}
Submitted summary: {{summary}}
Equipment or system identifiers: {{identifiers}}

Draft content:
{{draft_body}}

Return:
1. A suggested category from the allowed list.
2. A factual summary of no more than 500 characters.
3. Up to eight missing-information questions.
4. Up to ten search aliases.
5. Up to five possible duplicate-search phrases.
6. Sensitive-data flags.
7. Confidence from 0 to 1.
8. A concise reasoning summary suitable for a human reviewer.

The structured response schema is:

{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "suggested_category",
    "suggested_summary",
    "missing_information",
    "search_aliases",
    "duplicate_search_terms",
    "sensitive_data_flags",
    "confidence",
    "reasoning_summary"
  ],
  "properties": {
    "suggested_category": {
      "type": "string",
      "enum": [
        "Manufacturing",
        "Field Service",
        "Quality",
        "IT",
        "Safety",
        "General Operations"
      ]
    },
    "suggested_summary": {
      "type": "string",
      "maxLength": 500
    },
    "missing_information": {
      "type": "array",
      "maxItems": 8,
      "items": {
        "type": "string"
      }
    },
    "search_aliases": {
      "type": "array",
      "maxItems": 10,
      "items": {
        "type": "string"
      }
    },
    "duplicate_search_terms": {
      "type": "array",
      "maxItems": 5,
      "items": {
        "type": "string"
      }
    },
    "sensitive_data_flags": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "none",
          "credentials",
          "personal_data",
          "customer_confidential",
          "security_sensitive",
          "regulated_data",
          "other"
        ]
      }
    },
    "confidence": {
      "type": "number",
      "minimum": 0,
      "maximum": 1
    },
    "reasoning_summary": {
      "type": "string",
      "maxLength": 600
    }
  }
}

Configure a Zapier AI connector approved by the company, map only permitted fields, request structured output, and validate the result before updating Notion. If the selected connector cannot enforce a JSON schema, add a validation code step that parses the JSON and rejects extra or missing keys.

Store the AI provider’s request identifier, model identifier, processing timestamp, result status, and estimated usage where available. Do not log the complete prompt when it contains information the monitoring audience should not see.

Benefits of the AI Enhancement

  • Owners spend less time converting rough notes into a short searchable summary.
  • Category suggestions are more consistent when submitters use informal language.
  • Missing prerequisites, verification steps, and source references can be highlighted earlier.
  • Search aliases improve retrieval when equipment names and employee terminology differ.
  • Potentially sensitive content can be flagged for human review before broader publication.
  • Duplicate-search phrases help reviewers locate related content, although they do not replace exact duplicate checks.

These benefits are specific to interpreting unstructured text. They are separate from the assignment, reminders, approval controls, idempotency, publication status, and audit evidence delivered by the core automation.

What Remains Rule-Based or Human-Controlled

  • Final category and owner: Controlled mappings and the knowledge coordinator determine accountability.
  • Operational correctness: A qualified subject-matter expert verifies procedures and troubleshooting steps.
  • Safety conclusions: AI cannot approve safety instructions or determine that a procedure is safe.
  • Publication: A designated reviewer must approve content before it becomes Published.
  • Access permissions: IT and data owners determine who can view restricted content.
  • Policy exceptions: Managers and relevant control functions approve exceptions.
  • Retirement: A human confirms that content is obsolete, replaced, or no longer required.
  • Urgent corrections: A reviewer decides whether current guidance should be withdrawn.
  • Credential handling: Secrets are excluded rather than classified or rewritten by AI.

These decisions have operational, security, or safety consequences. Human accountability and deterministic controls are therefore required even when AI suggestions appear confident.

Estimating the Additional Value of AI

The AI enhancement applies to the 25 new submissions per month, not all 65 workflow events.

Representative AI value assumptions
Measure Manual process Core automation Core automation with AI
Submission triage and preparation 18 minutes 10 minutes 6 minutes including human AI review
Monthly new submissions 25 25 25
Expected AI correction rate Not applicable Not applicable 15 percent requiring 3 extra minutes
Expected AI service failure rate Not applicable Not applicable 5 percent requiring a 6-minute fallback
Representative AI usage cost $0 $0 $15 per month

Initial additional time recovered is 25 × 4 minutes ÷ 60 = 1.67 hours.

Expected correction time is 25 × 15% × 3 minutes ÷ 60 = 0.19 hours.

Expected failure fallback time is 25 × 5% × 6 minutes ÷ 60 = 0.13 hours.

Net additional capacity is approximately 1.67 – 0.19 – 0.13 = 1.35 hours per month.

At $55 per hour, the additional labour value is approximately $74.25. After the assumed $15 AI usage cost, net additional value is approximately $59.25 per month. This modest estimate supports AI only when it also improves consistency and completeness. It does not justify removing human review.

Testing Checklist

Use synthetic sample data before processing real operational information.

Required implementation tests
Test Expected result
Normal submission One draft is created with the correct owner, reviewer, page body, and Slack message.
Missing required field Form or Zap validation prevents incomplete creation.
Invalid category or select value Submission enters manual review and does not create an invalid option.
Duplicate submission Reviewer can identify and reject or merge semantically duplicate content.
Duplicate event Existing Knowledge ID is returned without a second Notion page.
Failed authentication Run stops, administrator is alerted, and no false success is recorded.
Expired or rotated credential Connection is replaced and the failed event can be replayed.
Failed API request Temporary failures retry; permanent failures enter the error queue.
Unavailable approver Backup assignment and escalation procedures work.
Rejection Reason is required and the article remains outside published views.
Reassignment New owner receives notification and the change is logged.
Overdue item Daily query selects the correct article based on review date.
Reminder spacing No repeat reminder is sent before the configured gap.
Escalation Reviewer and manager paths activate at the expected overdue thresholds.
Failed file upload or inaccessible link Publication is blocked until access is corrected.
Failed document creation Form response remains available for recovery and no duplicate is created on replay.
Failed notification Last Reminder is not falsely updated and the run is recoverable.
Unauthorized user Restricted content and administrative views remain inaccessible.
Malformed AI output Validation rejects the output and normal human processing continues.
Inaccurate AI output Owner can reject suggestions without changing authoritative fields.
AI service failure Draft creation and review continue without AI fields.
Successful approval Review log, dates, status, reviewer, and notification are complete.
Retirement Article leaves published views and includes reason and replacement where applicable.
Reporting Views and counts match controlled sample records.
Audit record Source timestamp, reviewer, decision, event key, and run ID are retrievable.
Retry behavior Temporary errors retry without creating duplicate records or messages.

Ongoing Maintenance

The operations systems analyst is the primary knowledge system owner. The IT administrator owns credentials, integration reliability, and security. A department manager acts as backup business owner.

Knowledge base maintenance schedule
Frequency Maintenance activity Owner
Daily Review failed runs, urgent feedback, stuck approvals, and scheduled review errors. Knowledge coordinator and IT
Weekly Reconcile Form submissions against Knowledge IDs and review unassigned records. Knowledge coordinator
Monthly Review task usage, automation cost, overdue content, inactive owners, and category mappings. Knowledge coordinator and IT
Quarterly Test backup exports, sample permissions, inspect API behavior, and retest critical automation paths. IT administrator
Quarterly Sample AI outputs for accuracy, correction rate, sensitive-data handling, and cost. Knowledge coordinator and AI governance owner
Semiannually Review user groups, integration editors, shared links, restricted data sources, and former-user access. IT and security owner
Annually Review retention rules, retired content, templates, governance documentation, and upgrade criteria. Operations and IT leadership
After platform change Retest API versions, field mappings, triggers, permissions, and error handling. IT administrator

Credential rotation should follow company policy and vendor capabilities. After rotation, run a controlled create, query, update, and Slack notification test. Deactivate old credentials only after confirming that production connections use the replacement.

Changes to property names, select values, Form questions, Slack channels, or category assignments require integration testing. Documentation must identify every automation that depends on each controlled value.

When to Move to Dedicated Software

The Notion-centered implementation remains appropriate while content volume, permissions, and workflows remain manageable. Dedicated knowledge management, document control, manufacturing execution, quality management, or service management software should be evaluated when several of the following conditions appear:

  • Transaction and article volume regularly exceed the practical monitoring capacity of the small administration team.
  • Complex field-level, record-level, customer-level, or location-level permissions become mandatory.
  • Formal regulatory controls require validated electronic signatures, controlled document issuance, training attestations, or immutable audit evidence.
  • Articles must integrate deeply with manufacturing, asset, ticketing, quality, or service systems.
  • Multiple plants or business units need independent governance and shared corporate content.
  • Exception rates and manual recovery effort continue to increase.
  • Notion structure or API maintenance consumes more time than the process saves.
  • Advanced analytics, search relevance controls, multilingual publishing, or content personalization become essential.
  • Technicians require purpose-built mobile, offline, or low-connectivity access.
  • Customers, suppliers, or contractors require a secure external portal.
  • Vendor support, service commitments, legal holds, or formal records management become mandatory.
  • Security risk exceeds what the current workspace and automation permission model can reasonably control.

Growth alone does not require replacement. The company can first improve ownership, split restricted content, archive old records, increase monitoring, or move selected workflows to dedicated systems while retaining Notion for general internal guidance.

Implementation Checklist

  • Document business requirements, content categories, owners, reviewers, and review intervals.
  • Confirm Notion, Google Forms, Zapier, Slack, Google Sheets, and Google Drive responsibilities.
  • Create managed automation accounts and backup ownership.
  • Configure least-privilege groups, teamspaces, folders, channels, and integration access.
  • Create standard and restricted Notion data sources.
  • Create exact fields, select values, relations, formulas, and audit properties.
  • Build the submission and feedback Forms with validation and privacy notices.
  • Configure category-to-owner, reviewer, interval, and Slack channel mappings.
  • Document every source-to-destination field mapping.
  • Implement deterministic Knowledge IDs and duplicate-event protection.
  • Build draft creation, feedback, publication, review reminder, escalation, and error workflows.
  • Define approval, rejection, reassignment, and retirement rules.
  • Configure Slack fallback paths for missing users and failed notifications.
  • Establish Notion page templates and controlled Google Drive file conventions.
  • Create published, review-due, incomplete, exception, failure, owner, and retirement views.
  • Configure API authentication, retry behavior, pagination, logging, and manual recovery.
  • Test standard, failure, permission, duplicate, reminder, escalation, and audit scenarios.
  • Pilot one category before migrating the full content library.
  • Document activation order, monitoring ownership, rollback, and support procedures.
  • Validate representative software, labour, implementation, and maintenance costs.
  • Replace savings assumptions with actual workflow volumes and handling times.
  • Add AI only after core automation is reliable and approved data controls are in place.
  • Assign primary and backup maintenance owners.
  • Define measurable criteria for moving selected workflows to dedicated software.

Get a FREE
Proof of Concept
& Consultation

No Cost, No Commitment!