AutomationFlowsSlack & Telegram › Draft Security Questionnaire Answers with Notion, Google Docs, and Slack

Draft Security Questionnaire Answers with Notion, Google Docs, and Slack

ByDyogo Henrique de Oliveira Ortega @holvvx on n8n.io

This workflow runs on demand to match security questionnaire questions against an approved-answer library in Notion, then creates a Google Docs draft and notifies reviewers in Slack, optionally updating a Notion tracker page. Starts manually to run a test questionnaire and load…

Event trigger★★★★☆ complexity18 nodesHTTP Request
Slack & Telegram Trigger: Event Nodes: 18 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #17098 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "foMTxxjgBUkGq8mo",
  "name": "Draft security questionnaire answers with Notion, Google Docs and Slack",
  "tags": [],
  "nodes": [
    {
      "id": "b78f18fc-448f-4480-8bc1-ff7b479a8721",
      "name": "Overview - official template description",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        32
      ],
      "parameters": {
        "color": 4,
        "width": 3860,
        "height": 620,
        "content": "## Draft security questionnaire answers with Notion, Google Docs and Slack\n\n### How it works\n\nThis workflow exports security questionnaire answers from Notion into a Google Docs review draft. It loads configured questionnaire details, fetches approved answers from Notion, matches them to the questionnaire, and branches depending on whether review is needed. It then creates a Google Docs draft, alerts the security reviewer in Slack, and optionally updates the Notion tracker.\n\n### Setup steps\n\n- Configure the `Set Workflow Variables` node with the correct Notion database ID, Google Drive folder ID, optional Google Docs template ID, Slack review channel ID, minimum confidence threshold, reviewer email, and optional tracker page ID.\n- Add Notion credentials with permission to query the approved-answer database and update the tracker page if you keep that optional node enabled.\n- Add Google Docs OAuth credentials with permission to create review draft documents.\n- Add Slack OAuth credentials with `chat:write` permission and invite the bot to the configured review channel.\n- Test the Notion database schema before using real questionnaires. The approved answer library should include question, answer, category, owner, and status fields.\n\n### Requirements\n\nYou need a Notion database of approved security answers, Google Docs access, Slack access, and an internal reviewer responsible for checking low-confidence or sensitive answers.\n\n### Customization\n\nReplace the sample questionnaire loader with Google Drive, Gmail, form intake, or CRM input. Adjust the matching rules, confidence threshold, Google Docs body, Slack message, and Notion tracker fields to match your review process.\n\n### Safety notes\n\nThis workflow must not invent questionnaire answers. Low-confidence matches stay marked as `Needs review`, and every draft should be reviewed before being sent externally."
      },
      "typeVersion": 1
    },
    {
      "id": "07759582-ab03-48b1-bf52-1e396aab1cdb",
      "name": "Step 1 - Start and load questionnaire",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        760
      ],
      "parameters": {
        "color": 5,
        "width": 600,
        "height": 460,
        "content": "## Start and configure\n\nA manual trigger starts the workflow, then shared IDs and channel settings are loaded for the Notion database, Google Docs destination, optional template, reviewer email, and Slack review channel."
      },
      "typeVersion": 1
    },
    {
      "id": "d90b68f9-a2da-411b-b7d1-60130e9de58b",
      "name": "Step 2 - Configure variables",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        832,
        760
      ],
      "parameters": {
        "color": 6,
        "width": 560,
        "height": 460,
        "content": "## Configure variables\n\nUpdate the `Set Workflow Variables` node first. Keep IDs, thresholds, customer name, tracker page, and channel settings here so users do not need to search through the workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "acc7fc1d-eb78-4a94-9291-39102803c621",
      "name": "Step 3 - Fetch approved answers",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1440,
        760
      ],
      "parameters": {
        "color": 5,
        "width": 760,
        "height": 460,
        "content": "## Load source answers\n\nThis section prepares questionnaire questions and queries Notion for approved answer records. Only reviewed answers should be used to populate the response draft."
      },
      "typeVersion": 1
    },
    {
      "id": "3bad29f3-7c84-4027-b43c-1c87ccc55888",
      "name": "Step 4 - Match and flag gaps",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2272,
        760
      ],
      "parameters": {
        "color": 5,
        "width": 700,
        "height": 648,
        "content": "## Match and review branch\n\nThe workflow matches questionnaire questions against the approved Notion answer library, checks whether any answers need review, and keeps low-confidence matches as `Needs review` instead of filling them automatically."
      },
      "typeVersion": 1
    },
    {
      "id": "c4135dcb-122f-4b5d-abbd-884b1c332258",
      "name": "Step 5 - Create review draft",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3024,
        760
      ],
      "parameters": {
        "color": 5,
        "width": 1000,
        "height": 760,
        "content": "## Create document, notify, and update tracker\n\nThis section builds the Google Docs draft, creates the review document, notifies the security reviewer in Slack, and optionally updates the related Notion tracker page with the draft status and number of answers needing review."
      },
      "typeVersion": 1
    },
    {
      "id": "510f40a8-92c6-4820-a1f6-c4310e9f4ef7",
      "name": "Security warning",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        1272
      ],
      "parameters": {
        "color": 3,
        "width": 1232,
        "height": 140,
        "content": "## Security warning\n\nSecurity questionnaires can include sensitive product, infrastructure, legal, and compliance details. Keep the draft in a restricted folder, do not hardcode secrets, and require human review before sending responses externally."
      },
      "typeVersion": 1
    },
    {
      "id": "27f56b4f-64de-4749-ba76-fe8d1259d158",
      "name": "Start Manual Test Run",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        400,
        952
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "8cde0cc8-4efd-409e-b294-745b78776afb",
      "name": "Set Workflow Variables",
      "type": "n8n-nodes-base.set",
      "notes": "Update these placeholder values before activating the workflow. Keep credentials in n8n credentials, not in fields or HTTP headers.",
      "position": [
        1056,
        952
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d5135515-812d-4fd0-adfe-6505d10c2da2",
              "name": "notion_database_id",
              "type": "string",
              "value": "your-notion-approved-answers-database-id"
            },
            {
              "id": "19d2c73c-0a8e-4c2c-b89f-30fd7178622e",
              "name": "google_drive_folder_id",
              "type": "string",
              "value": "your-google-drive-folder-id"
            },
            {
              "id": "292a45bc-a9da-45d7-82d3-c86edb17465b",
              "name": "google_doc_template_id",
              "type": "string",
              "value": "optional-google-doc-template-id"
            },
            {
              "id": "4c8e855d-8852-47de-9d71-9afde8169480",
              "name": "review_slack_channel_id",
              "type": "string",
              "value": "C0123456789"
            },
            {
              "id": "5e0c9999-e8bb-47cf-a163-94311e46ec34",
              "name": "minimum_match_confidence",
              "type": "number",
              "value": "0.70"
            },
            {
              "id": "c985dcd9-b8bf-41a9-bff6-c3b2f6ead7bf",
              "name": "approved_answer_status",
              "type": "string",
              "value": "Approved"
            },
            {
              "id": "56654d92-8a7d-45f0-bcfe-56aa75e2a97c",
              "name": "questionnaire_customer_name",
              "type": "string",
              "value": "Example Customer"
            },
            {
              "id": "fad926e5-d9a9-48af-8bf0-5b8bf9d4fde3",
              "name": "security_reviewer_email",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "14a85500-5543-4f8d-be59-8eaae7a9b5ea",
              "name": "notion_questionnaire_tracker_id",
              "type": "string",
              "value": "optional-notion-tracker-page-id"
            }
          ]
        },
        "includeOtherFields": true
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "39ac3f95-b3cf-46b6-99c9-6cd4b229f58c",
      "name": "Prepare Questionnaire Questions",
      "type": "n8n-nodes-base.code",
      "position": [
        1632,
        952
      ],
      "parameters": {
        "jsCode": "const item = $input.first().json;\nconst sampleQuestions = [\n  \"Do you encrypt customer data at rest?\",\n  \"Do you support SSO with SAML?\",\n  \"How do you manage access to production systems?\",\n  \"Do you have an incident response process?\",\n  \"Where is customer data hosted?\"\n];\n\nreturn [{\n  json: {\n    config: {\n      notion_database_id: item.notion_database_id,\n      google_drive_folder_id: item.google_drive_folder_id,\n      google_doc_template_id: item.google_doc_template_id,\n      review_slack_channel_id: item.review_slack_channel_id,\n      minimum_match_confidence: Number(item.minimum_match_confidence || 0.7),\n      approved_answer_status: item.approved_answer_status,\n      questionnaire_customer_name: item.questionnaire_customer_name,\n      security_reviewer_email: item.security_reviewer_email,\n      notion_questionnaire_tracker_id: item.notion_questionnaire_tracker_id,\n    },\n    questions: sampleQuestions.map((question, index) => ({ id: index + 1, question }))\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "69c1d9d1-3954-4e83-acd6-ad0480dbfd84",
      "name": "Fetch Approved Answers from Notion",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Select Notion credentials in this node. The Notion-Version header is public metadata, not a secret.",
      "position": [
        1920,
        952
      ],
      "parameters": {
        "url": "={{ \"https://api.notion.com/v1/databases/\" + $json.config.notion_database_id + \"/query\" }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={{\n{\n  \"filter\": {\n    \"property\": \"Status\",\n    \"select\": {\n      \"equals\": $json.config.approved_answer_status\n    }\n  },\n  \"page_size\": 100\n}\n}}",
        "sendBody": true,
        "contentType": "json",
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Notion-Version",
              "value": "2022-06-28"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "8daef7f2-75c1-4bc5-a4ff-94d9a21d1cca",
      "name": "Match Questions to Approved Answers",
      "type": "n8n-nodes-base.code",
      "notes": "Replace the sample approved answer array with parsed Notion results when adapting the workflow to your database schema.",
      "position": [
        2400,
        952
      ],
      "parameters": {
        "jsCode": "// Replace the sample answer library with the Notion response after testing.\n// This deterministic matcher avoids invented answers and marks low-confidence matches for review.\nconst input = $input.first().json;\nconst cfg = input.config || $('Prepare Questionnaire Questions').first().json.config;\nconst questions = input.questions || $('Prepare Questionnaire Questions').first().json.questions;\n\nconst approvedAnswers = [\n  {\n    category: 'Data protection',\n    question: 'Do you encrypt customer data at rest?',\n    answer: 'Yes. Customer data is encrypted at rest using managed cloud encryption controls. Confirm exact algorithms and key management details during review.',\n    owner: 'Security'\n  },\n  {\n    category: 'Identity and access',\n    question: 'Do you support SSO with SAML?',\n    answer: 'Yes. SAML SSO is supported for eligible plans. Confirm IdP-specific instructions with the implementation team.',\n    owner: 'Product'\n  },\n  {\n    category: 'Access management',\n    question: 'How do you manage access to production systems?',\n    answer: 'Production access is restricted to authorized personnel and reviewed periodically. Add your internal review cadence before sending.',\n    owner: 'Security'\n  },\n  {\n    category: 'Incident response',\n    question: 'Do you have an incident response process?',\n    answer: 'Yes. The company maintains an incident response process with defined triage, escalation, communication, and post-incident review steps.',\n    owner: 'Security'\n  }\n];\n\nfunction tokenize(s) {\n  return new Set(String(s).toLowerCase().replace(/[^a-z0-9\\s]/g, ' ').split(/\\s+/).filter(w => w.length > 2));\n}\nfunction jaccard(a, b) {\n  const A = tokenize(a), B = tokenize(b);\n  const inter = [...A].filter(x => B.has(x)).length;\n  const union = new Set([...A, ...B]).size || 1;\n  return inter / union;\n}\n\nconst matched = questions.map(q => {\n  let best = null;\n  let score = 0;\n  for (const a of approvedAnswers) {\n    const s = jaccard(q.question, a.question);\n    if (s > score) { score = s; best = a; }\n  }\n  const approved = best && score >= cfg.minimum_match_confidence;\n  return {\n    id: q.id,\n    question: q.question,\n    answer: approved ? best.answer : 'Needs review',\n    category: approved ? best.category : 'Unmatched',\n    owner: approved ? best.owner : cfg.security_reviewer_email,\n    confidence: Number(score.toFixed(2)),\n    needsReview: !approved\n  };\n});\n\nreturn [{\n  json: {\n    config: cfg,\n    matchedAnswers: matched,\n    needsReviewCount: matched.filter(x => x.needsReview).length,\n    totalQuestions: matched.length\n  }\n}];"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "b468a8ff-b218-4247-8900-e1cb42c473b3",
      "name": "Check Answers for Review",
      "type": "n8n-nodes-base.if",
      "position": [
        2672,
        952
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "6bf7978d-8dc6-4a89-bfbf-81510adeaeed",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.needsReviewCount > 0 }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "2cf4a0ed-2fbe-41cb-88fa-830db8d47399",
      "name": "Create Google Docs Draft Body",
      "type": "n8n-nodes-base.code",
      "notes": "Customize this formatter to match your security review style and required sections.",
      "position": [
        3168,
        1048
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst cfg = data.config;\nconst lines = [];\nlines.push(`# Security questionnaire draft - ${cfg.questionnaire_customer_name}`);\nlines.push('');\nlines.push('Review all answers before sending this document externally.');\nlines.push('');\nfor (const item of data.matchedAnswers) {\n  lines.push(`## ${item.id}. ${item.question}`);\n  lines.push(`Category: ${item.category}`);\n  lines.push(`Owner: ${item.owner}`);\n  lines.push(`Confidence: ${item.confidence}`);\n  lines.push('');\n  lines.push(item.answer);\n  lines.push('');\n  if (item.needsReview) lines.push('Reviewer note: This answer needs manual review before submission.\\n');\n}\nreturn [{ json: { ...data, documentMarkdown: lines.join('\\n') } }];"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "c98f038e-f2d8-4b30-a032-feacd2538b47",
      "name": "Create Google Docs Review Draft",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Select Google Docs OAuth credentials. After creating the doc, add content with batchUpdate in your final setup.",
      "position": [
        3440,
        1048
      ],
      "parameters": {
        "url": "https://docs.googleapis.com/v1/documents",
        "method": "POST",
        "options": {},
        "jsonBody": "={{\n{\n  \"title\": \"Security questionnaire draft - \" + $json.config.questionnaire_customer_name\n}\n}}",
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json"
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "1dc47959-7e68-4a97-8ba9-cce6068af742",
      "name": "Notify Security Reviewer in Slack",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Select Slack OAuth credentials. Never hardcode bot tokens.",
      "position": [
        3744,
        1048
      ],
      "parameters": {
        "url": "https://slack.com/api/chat.postMessage",
        "method": "POST",
        "options": {},
        "jsonBody": "={{\n{\n  \"channel\": $json.config.review_slack_channel_id,\n  \"text\": \"Security questionnaire draft created for \" + $json.config.questionnaire_customer_name + \". \" + $json.needsReviewCount + \" answers need review.\"\n}\n}}",
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json"
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "4510147f-3ec5-4e66-93b0-2cc9c3909961",
      "name": "Update Questionnaire Tracker in Notion",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Optional. Disable this node if you do not track questionnaires in Notion.",
      "position": [
        3744,
        1272
      ],
      "parameters": {
        "url": "={{ \"https://api.notion.com/v1/pages/\" + $json.config.notion_questionnaire_tracker_id }}",
        "method": "PATCH",
        "options": {},
        "jsonBody": "={{\n{\n  \"properties\": {\n    \"Status\": {\n      \"select\": {\n        \"name\": \"Draft ready for review\"\n      }\n    },\n    \"Needs review\": {\n      \"number\": $json.needsReviewCount\n    }\n  }\n}\n}}",
        "sendBody": true,
        "contentType": "json",
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Notion-Version",
              "value": "2022-06-28"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "29623719-0d19-448d-a1c6-b0892ba8d1f4",
      "name": "Notify Full-Match Draft Ready",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "This branch is used when no low-confidence answers were detected.",
      "position": [
        2672,
        1176
      ],
      "parameters": {
        "url": "https://slack.com/api/chat.postMessage",
        "method": "POST",
        "options": {},
        "jsonBody": "={{\n{\n  \"channel\": $json.config.review_slack_channel_id,\n  \"text\": \"Security questionnaire draft has no low-confidence matches, but still requires final human review.\"\n}\n}}",
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json"
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "729d9d2f-a7a5-4078-b192-2be9d20c04c7",
  "connections": {
    "Start Manual Test Run": {
      "main": [
        [
          {
            "node": "Set Workflow Variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Workflow Variables": {
      "main": [
        [
          {
            "node": "Prepare Questionnaire Questions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Answers for Review": {
      "main": [
        [
          {
            "node": "Create Google Docs Draft Body",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notify Full-Match Draft Ready",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Google Docs Draft Body": {
      "main": [
        [
          {
            "node": "Create Google Docs Review Draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Full-Match Draft Ready": {
      "main": [
        [
          {
            "node": "Create Google Docs Draft Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Google Docs Review Draft": {
      "main": [
        [
          {
            "node": "Notify Security Reviewer in Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Questionnaire Questions": {
      "main": [
        [
          {
            "node": "Fetch Approved Answers from Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Security Reviewer in Slack": {
      "main": [
        [
          {
            "node": "Update Questionnaire Tracker in Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Approved Answers from Notion": {
      "main": [
        [
          {
            "node": "Match Questions to Approved Answers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Match Questions to Approved Answers": {
      "main": [
        [
          {
            "node": "Check Answers for Review",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow runs on demand to match security questionnaire questions against an approved-answer library in Notion, then creates a Google Docs draft and notifies reviewers in Slack, optionally updating a Notion tracker page. Starts manually to run a test questionnaire and load…

Source: https://n8n.io/workflows/17098/ — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

Worflow. Uses whatsAppTrigger, httpRequest, whatsApp. Event-driven trigger; 94 nodes.

WhatsApp Trigger, HTTP Request, WhatsApp
Slack & Telegram

Webhook Slack. Uses theHiveProjectTrigger, stickyNote, httpRequest, theHiveProject. Event-driven trigger; 63 nodes.

The Hive Project Trigger, HTTP Request, The Hive Project +1
Slack & Telegram

Key Features: Direct Case Management: Modify case details such as assignee, severity, status, and more through intuitive form inputs embedded within Slack messages. Seamless Integration: Assumes match

The Hive Project Trigger, HTTP Request, The Hive Project +1
Slack & Telegram

N8N Complete Final. Uses telegramTrigger, dataTable, telegram, mqtt. Event-driven trigger; 58 nodes.

Telegram Trigger, Data Table, Telegram +3
Slack & Telegram

Pede Ai. Uses httpRequest, telegram, postgres, telegramTrigger. Event-driven trigger; 57 nodes.

HTTP Request, Telegram, Postgres +1