AutomationFlowsEmail & Gmail › Chase Missing Onboarding Documents Daily with Google Drive, Gmail and Slack

Chase Missing Onboarding Documents Daily with Google Drive, Gmail and Slack

Chase missing onboarding documents daily with Google Drive, Gmail and Slack. Uses googleDrive, dataTable, slack, gmail. Scheduled trigger; 21 nodes.

Cron / scheduled trigger★★★★☆ complexity21 nodesGoogle DriveData TableSlackGmail
Email & Gmail Trigger: Cron / scheduled Nodes: 21 Complexity: ★★★★☆ Added:

This workflow follows the Datatable → Gmail recipe pattern — see all workflows that pair these two integrations.

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
{
  "name": "Chase missing onboarding documents daily with Google Drive, Gmail and Slack",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "id": "cfa5de40-1305-4b09-bdbc-ce6b933a5b41",
      "name": "Daily 08:00 Chase Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        16,
        -128
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "cfg-templates-folder",
              "name": "templates_folder_id",
              "value": "<__PLACEHOLDER_VALUE__DRIVE_TEMPLATES_FOLDER_ID__>",
              "type": "string"
            },
            {
              "id": "cfg-slack-channel",
              "name": "slack_escalation_channel_id",
              "value": "<__PLACEHOLDER_VALUE__SLACK_ESCALATION_CHANNEL_ID__>",
              "type": "string"
            },
            {
              "id": "cfg-reply-to",
              "name": "coordinator_reply_to",
              "value": "<__PLACEHOLDER_VALUE__COORDINATOR_REPLY_TO_EMAIL__>",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "988b0072-32b2-42eb-858b-da0de4a515f7",
      "name": "Workflow Configuration",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        288,
        -128
      ]
    },
    {
      "parameters": {
        "resource": "fileFolder",
        "returnAll": true,
        "filter": {
          "folderId": {
            "__rl": true,
            "mode": "id",
            "value": "={{ $json.templates_folder_id }}"
          },
          "whatToSearch": "files"
        },
        "options": {}
      },
      "id": "7d70d51b-fa8b-44a2-ac6b-dfaed7a42cdd",
      "name": "List Blank Form Templates",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        560,
        -128
      ],
      "alwaysOutputData": true,
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "name",
          "value": "Paperwork Requirement Matrix",
          "cachedResultName": null
        },
        "returnAll": true
      },
      "id": "0f309fb9-3788-443d-8de9-684e71b8e216",
      "name": "Fetch Requirement Matrix",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        832,
        -128
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "name",
          "value": "Paperwork Chase Roster",
          "cachedResultName": null
        },
        "filters": {
          "conditions": [
            {
              "keyName": "docs_complete",
              "condition": "isFalse"
            },
            {
              "keyName": "docs_complete",
              "condition": "isEmpty"
            }
          ]
        },
        "returnAll": true
      },
      "id": "788893b0-c9bd-4c8e-b0c5-452c1fe72c5a",
      "name": "Fetch Onboarding Roster",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        1104,
        -128
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "jsCode": "// Gather every loop pass of the reconcile node and keep only people whose tier is overdue today.\nconst byEmail = {};\nlet run = 0;\nwhile (run < 1000) {\n  let items = null;\n  try {\n    items = $('Compute Chase Tier And Gaps').all(0, run);\n  } catch (e) {\n    break;\n  }\n  if (!items) break;\n  for (const it of items) {\n    const j = it.json || {};\n    if (j.is_overdue && j.email && !byEmail[j.email]) byEmail[j.email] = j;\n  }\n  run = run + 1;\n}\nconst overdue = Object.values(byEmail);\nif (overdue.length === 0) {\n  return [];\n}\nconst lines = overdue.map(function (p) {\n  return '- ' + String(p.name || 'Unknown') + ' (' + p.email + '): ' + String(p.missing_now || 'gaps not listed');\n});\nconst text = 'Paperwork chase: ' + overdue.length + ' person(s) overdue today.\\n' + lines.join('\\n') + '\\nChase emails went out on the overdue cadence. This is the daily roll up.';\nreturn [{ json: { escalation_text: text, overdue_count: overdue.length } }];"
      },
      "id": "7f587754-b36b-45cf-8a3b-9a5aa0f593e9",
      "name": "Collect Overdue Escalations",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1712,
        352
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Workflow Configuration').first().json.slack_escalation_channel_id }}"
        },
        "text": "={{ $json.escalation_text }}",
        "otherOptions": {}
      },
      "id": "fce9f4d0-479c-4767-8fd2-cfcac7f0dc82",
      "name": "Post Overdue Escalation To Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        2000,
        352
      ],
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "fileFolder",
        "returnAll": true,
        "filter": {
          "folderId": {
            "__rl": true,
            "mode": "id",
            "value": "={{ $json.drive_folder_id }}"
          },
          "whatToSearch": "files"
        },
        "options": {}
      },
      "id": "6d65f930-1cc1-405d-a298-d36c4c0fb485",
      "name": "List Person Intake Folder",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1728,
        -112
      ],
      "alwaysOutputData": true,
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "// One roster row per loop pass. Reconcile the intake folder against the matrix and score the cadence.\nconst person = $('Loop People One by One').first().json;\nconst matrixRows = $('Fetch Requirement Matrix').all().map(function (i) { return i.json; });\nconst templates = $('List Blank Form Templates').all().map(function (i) { return i.json; }).filter(function (t) { return t && t.id && t.name; });\n\nconst files = $input.all().map(function (i) { return i.json; }).filter(function (f) { return f && f.name; });\nconst fileNames = files.map(function (f) { return String(f.name).toLowerCase(); });\n\nconst personRole = String(person.role || '').toLowerCase();\nconst required = matrixRows.filter(function (r) { return String(r.role || '').toLowerCase() === personRole; });\n\nconst today = $today;\nconst startDate = DateTime.fromISO(String(person.start_date || ''));\n\n// Case insensitive, extension agnostic match: the uploaded file name only has to contain the slug.\nconst missing = [];\nfor (const req of required) {\n  const slug = String(req.doc_slug || '').toLowerCase();\n  if (!slug) continue;\n  const have = fileNames.some(function (n) { return n.includes(slug); });\n  if (have) continue;\n  const offset = Number(req.due_offset_days) || 0;\n  const due = startDate.isValid ? startDate.plus({ days: offset }) : today;\n  const daysLeft = Math.floor(due.diff(today, 'days').days);\n  missing.push({ slug: slug, label: String(req.doc_label || req.doc_slug), due_date: due.toISODate(), days_left: daysLeft });\n}\n\nconst isComplete = required.length > 0 && missing.length === 0;\n\n// Tier by the most urgent missing document.\nlet stage = 'waiting';\nif (isComplete) {\n  stage = 'complete';\n} else if (missing.some(function (m) { return m.days_left < 0; })) {\n  stage = 'overdue';\n} else if (missing.some(function (m) { return m.days_left <= 2; })) {\n  stage = 'firm';\n} else if (missing.some(function (m) { return m.days_left <= 7; })) {\n  stage = 'gentle';\n}\n\n// last_chased suppresses repeat emails inside each cadence window.\nconst cooldown = { gentle: 3, firm: 2, overdue: 1 };\nlet shouldChase = stage === 'gentle' || stage === 'firm' || stage === 'overdue';\nif (shouldChase && person.last_chased) {\n  const lastChased = DateTime.fromISO(String(person.last_chased));\n  if (lastChased.isValid) {\n    const daysSince = Math.floor(today.diff(lastChased.startOf('day'), 'days').days);\n    if (daysSince < cooldown[stage]) shouldChase = false;\n  }\n}\n\n// One blank form template per missing document, matched by slug substring on the template file name.\nconst matchedTemplates = [];\nif (shouldChase) {\n  for (const m of missing) {\n    const t = templates.find(function (tp) { return String(tp.name).toLowerCase().includes(m.slug); });\n    if (t && !matchedTemplates.some(function (x) { return x.id === t.id; })) {\n      matchedTemplates.push({ id: t.id, name: t.name });\n    }\n  }\n}\n\nconst firstName = String(person.name || '').trim().split(' ')[0] || 'there';\nconst itemLines = missing.map(function (m) {\n  const tail = m.days_left < 0 ? Math.abs(m.days_left) + ' day(s) overdue' : 'due ' + m.due_date;\n  return '- ' + m.label + ' (' + tail + ')';\n}).join('\\n');\n\nlet subject = '';\nlet body = '';\nif (stage === 'gentle') {\n  subject = 'Quick reminder: a few onboarding documents to send in';\n  body = 'Hi ' + firstName + ',\\n\\nWelcome aboard! A quick heads up that we are still waiting on the following documents:\\n\\n' + itemLines + '\\n\\nBlank copies of the forms are attached. Just drop the completed files into your intake folder whenever you have a moment.\\n\\nThanks!';\n} else if (stage === 'firm') {\n  subject = 'Action needed: onboarding documents due soon';\n  body = 'Hi ' + firstName + ',\\n\\nThe following documents are due within the next two days:\\n\\n' + itemLines + '\\n\\nBlank copies are attached. Please upload the completed files to your intake folder so everything is ready on time.\\n\\nThank you!';\n} else {\n  subject = 'Overdue: onboarding documents outstanding';\n  body = 'Hi ' + firstName + ',\\n\\nThe following documents are now overdue:\\n\\n' + itemLines + '\\n\\nBlank copies are attached. Please upload the completed files to your intake folder today, or reply to this email if something is blocking you.\\n\\nThank you!';\n}\n\nconst base = {\n  name: person.name,\n  email: person.email,\n  role: person.role,\n  start_date: person.start_date,\n  drive_folder_id: person.drive_folder_id,\n  is_complete: isComplete,\n  should_chase: shouldChase,\n  chase_stage: stage,\n  new_last_chased: shouldChase ? today.toISODate() : String(person.last_chased || ''),\n  missing_count: missing.length,\n  missing_now: missing.map(function (m) { return m.slug + ' (due ' + m.due_date + ')'; }).join(', '),\n  is_overdue: stage === 'overdue',\n  subject: subject,\n  email_body: body\n};\n\nif (!shouldChase || matchedTemplates.length === 0) {\n  return [{ json: Object.assign({}, base, { template_file_id: '', template_file_name: '' }) }];\n}\nreturn matchedTemplates.map(function (t) {\n  return { json: Object.assign({}, base, { template_file_id: t.id, template_file_name: t.name }) };\n});"
      },
      "id": "e1aae413-9b4c-49a5-ba14-185d87942be2",
      "name": "Compute Chase Tier And Gaps",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2000,
        -112
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "chase-flag",
              "leftValue": "={{ $json.should_chase }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "e0938204-fce8-479c-8064-f2a649bd9924",
      "name": "Should Chase This Person",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        2272,
        -112
      ]
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.template_file_id }}"
        },
        "options": {
          "binaryPropertyName": "data",
          "fileName": "={{ $json.template_file_name }}"
        }
      },
      "id": "eed4e146-a78a-42ef-b57e-77da04b41a8d",
      "name": "Download Blank Form PDF",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        96,
        320
      ],
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "// Consolidate every downloaded blank form into ONE item with form_0, form_1 style binary properties.\nconst meta = $('Compute Chase Tier And Gaps').first().json;\nconst out = { json: { send_to: meta.email, subject: meta.subject, email_body: meta.email_body, attachment_props: '' }, binary: {} };\nconst props = [];\nlet idx = 0;\nfor (const item of $input.all()) {\n  if (!item.binary) continue;\n  for (const key of Object.keys(item.binary)) {\n    const prop = 'form_' + idx;\n    out.binary[prop] = item.binary[key];\n    props.push(prop);\n    idx = idx + 1;\n  }\n}\nif (props.length === 0) {\n  // No blank form matched in the templates folder. Attach a plain checklist so the chase still lands.\n  const checklist = 'Missing documents for ' + String(meta.name || '') + '\\n\\n' + String(meta.missing_now || '') + '\\n\\nPlease upload the completed files to your intake folder.';\n  out.binary.form_0 = {\n    data: Buffer.from(checklist, 'utf8').toString('base64'),\n    mimeType: 'text/plain',\n    fileName: 'missing-documents.txt'\n  };\n  props.push('form_0');\n}\nout.json.attachment_props = props.join(',');\nreturn [out];"
      },
      "id": "66cc27ca-2355-48aa-9431-b2cc93864d81",
      "name": "Bundle Forms Into One Email",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        368,
        320
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $json.send_to }}",
        "subject": "={{ $json.subject }}",
        "emailType": "text",
        "message": "={{ $json.email_body }}",
        "options": {
          "appendAttribution": false,
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "={{ $json.attachment_props }}"
              }
            ]
          },
          "replyTo": "={{ $('Workflow Configuration').first().json.coordinator_reply_to }}"
        }
      },
      "id": "246d64a2-6f80-4d6a-821a-215ac2ca5d41",
      "name": "Send Chase Email",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        640,
        320
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "update",
        "dataTableId": {
          "__rl": true,
          "mode": "name",
          "value": "Paperwork Chase Roster",
          "cachedResultName": null
        },
        "matchType": "allConditions",
        "filters": {
          "conditions": [
            {
              "keyName": "email",
              "keyValue": "={{ $('Compute Chase Tier And Gaps').first().json.email }}"
            }
          ]
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "docs_complete": "={{ $('Compute Chase Tier And Gaps').first().json.is_complete }}",
            "last_chased": "={{ $('Compute Chase Tier And Gaps').first().json.new_last_chased }}",
            "chase_stage": "={{ $('Compute Chase Tier And Gaps').first().json.chase_stage }}",
            "missing_now": "={{ $('Compute Chase Tier And Gaps').first().json.missing_now }}"
          },
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "d06b8c5d-759f-4a6f-8d25-2df573d072f1",
      "name": "Write Person Status Back",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        912,
        320
      ],
      "alwaysOutputData": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "2ad2691a-c946-408b-a43f-8f439d8cc84c",
      "name": "Loop People One by One",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        1456,
        -128
      ]
    },
    {
      "parameters": {
        "content": "# Missing Paperwork Collector\n\nOne daily run chases exactly the documents each person still owes. It reads a role based requirement matrix and a people roster from n8n Data Tables, lists each person's Drive intake folder, and emails only the missing items with blank forms attached. Overdue cases roll up into a single Slack escalation.\n\n## How it works\n1. The schedule fires every morning at 08:00.\n2. Workflow Configuration holds the templates folder ID, the Slack channel ID, and the reply to address.\n3. The blank form templates folder is listed once per run.\n4. The requirement matrix (role, doc_slug, doc_label, due_offset_days) and the roster of people not yet complete load from Data Tables.\n5. Each person's intake folder is listed and filenames are matched against required slugs, case insensitive and extension agnostic.\n6. Code with Luxon works out the cadence: gentle from 7 days before due, firm from 2 days, escalation once overdue. last_chased suppresses repeat emails inside each cadence window.\n7. Chase emails name only that person's missing items with due dates, and attach the matching blank form PDFs as one email with multiple attachments.\n8. The roster row is updated: last_chased, chase_stage, missing_now, and docs_complete flips to true once the folder satisfies the matrix.\n9. After the loop, one Slack message lists only the overdue people and their specific gaps.\n\n## Setup\n1. Connect Google Drive, Gmail, and Slack credentials.\n2. Replace <__PLACEHOLDER_VALUE__DRIVE_TEMPLATES_FOLDER_ID__> with the Drive folder that holds blank form PDFs. Each file name must contain its doc slug, like waiver_blank.pdf.\n3. Replace <__PLACEHOLDER_VALUE__SLACK_ESCALATION_CHANNEL_ID__> with the channel for overdue escalations.\n4. Replace <__PLACEHOLDER_VALUE__COORDINATOR_REPLY_TO_EMAIL__> with the address replies should go to.\n5. Fill the Paperwork Requirement Matrix Data Table: one row per role and document, columns role, doc_slug, doc_label, due_offset_days from start_date (negative means due before day one).\n6. Fill the Paperwork Chase Roster Data Table: name, email, role, start_date (YYYY-MM-DD), drive_folder_id. Leave last_chased, docs_complete, chase_stage, missing_now empty.\n7. Uploaded files must contain the doc slug in the file name, like waiver_janedoe.pdf. Any extension works, matching ignores it.\n8. Keep intake folders flat. The listing reads direct children only.",
        "height": 812,
        "width": 1450
      },
      "id": "ca280d25-6349-4b41-9eda-adf339d43bd7",
      "name": "Overview And Setup",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -48,
        -1200
      ]
    },
    {
      "parameters": {
        "content": "## Schedule, config and reference data\n\nDaily 08:00 trigger. All user specific IDs live in Workflow Configuration. The blank form templates folder is listed once per run, and both Data Table reads pull every row with Execute Once on so the roster never multiplies per matrix row.",
        "height": 404,
        "width": 1354,
        "color": 7
      },
      "id": "8182ca41-321f-481f-83e8-3bb0578c89c8",
      "name": "Section Schedule And Reference Data",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -48,
        -320
      ]
    },
    {
      "parameters": {
        "content": "## Reconcile one person at a time\n\nThe loop takes one roster row per pass and lists that person's intake folder. Code with Luxon compares filenames to the matrix, scores the cadence tier, and applies the last_chased cooldown. The IF only lets real chase cases through, everyone else goes straight to the status write.",
        "height": 404,
        "width": 1110,
        "color": 7
      },
      "id": "f8711adf-1a1a-41c6-a3ec-9a71d2426078",
      "name": "Section Reconcile Each Person",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1360,
        -320
      ]
    },
    {
      "parameters": {
        "content": "## Chase email with blank forms\n\nEach missing document's blank PDF downloads as its own item, then Code consolidates them into one item with form_0, form_1 style binary properties for a single Gmail send. The send continues on error so one bad address never stops the roster, and the roster row updates either way.",
        "height": 388,
        "width": 1126,
        "color": 7
      },
      "id": "8a896b6c-03bf-4182-bec6-4c545115040c",
      "name": "Section Chase Email With Blank Forms",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        144
      ]
    },
    {
      "parameters": {
        "content": "## Overdue escalation\n\nAfter the loop, Code collects every person whose tier is overdue today and one Slack message lists their specific gaps. No overdue people means no message.",
        "height": 388,
        "width": 650,
        "color": 7
      },
      "id": "88267740-3559-4c0f-bf4b-ca79100c97c1",
      "name": "Section Overdue Escalation",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1600,
        144
      ]
    },
    {
      "parameters": {
        "content": "## Drive listing limits\n\n1. The folder listing returns DIRECT children only. Keep intake folders flat, files inside subfolders are invisible to the matcher.\n2. Return All stays on for both Drive listings. The default 50 item cap silently truncates bigger folders and makes documents look missing when they are not.\n3. Every doc_slug needs one blank PDF in the templates folder with the slug in its file name. If none match, the email falls back to a plain text checklist attachment.",
        "height": 252,
        "width": 700,
        "color": 3
      },
      "id": "218e1363-1dae-4c48-a382-efc81a0b4b98",
      "name": "Warning Drive Listing Limits",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1472,
        -720
      ]
    }
  ],
  "connections": {
    "Daily 08:00 Chase Trigger": {
      "main": [
        [
          {
            "node": "Workflow Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration": {
      "main": [
        [
          {
            "node": "List Blank Form Templates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List Blank Form Templates": {
      "main": [
        [
          {
            "node": "Fetch Requirement Matrix",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Requirement Matrix": {
      "main": [
        [
          {
            "node": "Fetch Onboarding Roster",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Onboarding Roster": {
      "main": [
        [
          {
            "node": "Loop People One by One",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect Overdue Escalations": {
      "main": [
        [
          {
            "node": "Post Overdue Escalation To Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List Person Intake Folder": {
      "main": [
        [
          {
            "node": "Compute Chase Tier And Gaps",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute Chase Tier And Gaps": {
      "main": [
        [
          {
            "node": "Should Chase This Person",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Should Chase This Person": {
      "main": [
        [
          {
            "node": "Download Blank Form PDF",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Write Person Status Back",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Blank Form PDF": {
      "main": [
        [
          {
            "node": "Bundle Forms Into One Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Bundle Forms Into One Email": {
      "main": [
        [
          {
            "node": "Send Chase Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Chase Email": {
      "main": [
        [
          {
            "node": "Write Person Status Back",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write Person Status Back": {
      "main": [
        [
          {
            "node": "Loop People One by One",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop People One by One": {
      "main": [
        [
          {
            "node": "Collect Overdue Escalations",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "List Person Intake Folder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "tags": []
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

Chase missing onboarding documents daily with Google Drive, Gmail and Slack. Uses googleDrive, dataTable, slack, gmail. Scheduled trigger; 21 nodes.

Source: https://github.com/exekyute/n8n-exekyute-templates/blob/main/published/n8n-missing-paperwork-collector/workflow.json — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

This workflow automates the complete end-to-end processing of daily revenue transactions for finance and accounting teams. It systematically retrieves, validates, and standardizes transaction data fro

HTTP Request, Gmail, Google Drive +2
Email & Gmail

Validate staff certifications against upcoming session dates using Google Calendar, Sheets, Slack and Gmail. Uses googleSheets, dataTable, googleCalendar, slack. Scheduled trigger; 24 nodes.

Google Sheets, Data Table, Google Calendar +2
Email & Gmail

Tags: Image Compression, Tinify API, TinyPNG, SEO Optimisation, E-commerce, Marketing

Google Drive, HTTP Request, Data Table +1
Email & Gmail

YOUR_ID 4. Uses gmail, googleDrive, googleSheets, httpRequest. Scheduled trigger; 53 nodes.

Gmail, Google Drive, Google Sheets +1
Email & Gmail

14310 Send Overdue Invoice Payment Reminders With Ifirma Gmail Postgrid And Slack. Uses httpRequest, stopAndError, slack, gmail. Scheduled trigger; 53 nodes.

HTTP Request, Stop And Error, Slack +1