{
  "name": "Client Document Chaser (Helm Systems)",
  "nodes": [
    {
      "parameters": {
        "content": "## Client Document Chaser\nFor accountants, bookkeepers, brokers, agents and anyone who waits on clients for paperwork.\n1. Make a Google Sheet with: Client, Email, Missing Items, Needed By, Nudges Sent, Last Nudge, Status. Put the missing items in one cell, separated by commas. Set Status to Done when everything is in.\n2. Pick that sheet on **Read requests** and **Mark nudged**. Fill in **Settings**. Connect Gmail and the AI model credentials.\n3. Each weekday at 9am it nudges clients who still owe documents, on a schedule you set, with a firmer tone each time. It lists exactly what is missing and nothing else.\n4. With autoSend off you get Gmail drafts to review first.\nMade by Helm Systems: helmsystems.biz/field-kit",
        "width": 420,
        "height": 340,
        "color": 4
      },
      "id": "6c71b8d4-744d-4744-a190-eb98d6c238f6",
      "name": "Note 1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -342
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "weeksInterval": 1,
              "triggerAtDay": [
                1,
                2,
                3,
                4,
                5
              ],
              "triggerAtHour": 9
            }
          ]
        }
      },
      "id": "a66e7d80-bc35-4b9a-a7a0-bcfc03810841",
      "name": "Every weekday at 9am",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "67cdd80d-9b8f-47c1-a9df-3a38cc65d612",
              "name": "business",
              "value": "Your Business Name",
              "type": "string"
            },
            {
              "id": "91e769df-8dd2-4cb6-ae96-96db771678bd",
              "name": "owner",
              "value": "Your Name",
              "type": "string"
            },
            {
              "id": "0d1887a5-d1c2-4d6f-a31a-ef95a0177fe3",
              "name": "ownerEmail",
              "value": "you@yourbusiness.com",
              "type": "string"
            },
            {
              "id": "404d8ffb-5fff-4557-a863-74e6ea96358f",
              "name": "autoSend",
              "value": false,
              "type": "boolean"
            },
            {
              "id": "d15e2fe8-dd11-465e-a77e-842f7a2d7b4b",
              "name": "waitDays",
              "value": 3,
              "type": "number"
            },
            {
              "id": "2509e3a9-fde2-4c54-a5da-96790f3bbc90",
              "name": "maxNudges",
              "value": 5,
              "type": "number"
            },
            {
              "id": "c099ea5c-8ba9-4276-a9e0-fb3fb517e516",
              "name": "consequence",
              "value": "",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "f5e4902b-472a-43c7-a0e3-4e9342be4c2e",
      "name": "Settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        260,
        0
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "options": {}
      },
      "id": "f26f08a9-fdc6-4adc-af8d-c5e3eb9e442d",
      "name": "Read requests",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        520,
        0
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const asDate = (v) => { if (!v) return null; const d = new Date(v); return isNaN(d) ? null : d; };\nconst startOfDay = (d) => { const x = new Date(d); x.setHours(0, 0, 0, 0); return x; };\nconst today = startOfDay(new Date());\nconst daysBetween = (a, b) => Math.round((b - a) / 86400000);\nconst s = $('Settings').first().json;\nconst out = [];\nfor (const item of $input.all()) {\n  const r = item.json;\n  const status = String(r['Status'] || '').trim().toLowerCase();\n  if (status === 'done') continue;\n  const missing = String(r['Missing Items'] || '').trim();\n  const email = String(r['Email'] || '').trim();\n  if (!missing || !email) continue;\n  const done = Number(r['Nudges Sent'] || 0);\n  if (done >= Number(s.maxNudges)) continue;\n  const last = asDate(r['Last Nudge']);\n  if (last && daysBetween(startOfDay(last), today) < Number(s.waitDays)) continue;\n  out.push({ json: {\n    row_number: r.row_number,\n    name: String(r['Client'] || '').split(' ')[0] || 'there',\n    email,\n    missing,\n    neededBy: r['Needed By'] || 'as soon as you can',\n    nudge: done + 1,\n    consequence: s.consequence || 'none',\n    business: s.business, owner: s.owner,\n  } });\n}\nreturn out;"
      },
      "id": "be662173-023e-41ab-af51-bdbd5a1599a3",
      "name": "Pick clients to nudge",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        780,
        0
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Client: {{ $json.name }}\nMissing items: {{ $json.missing }}\nNeeded by: {{ $json.neededBy }}\nThis is nudge number {{ $json.nudge }}.\nSign off as {{ $json.owner }}, {{ $json.business }}.",
        "hasOutputParser": false,
        "messages": {
          "messageValues": [
            {
              "message": "=You write document reminder emails for {{ $json.business }} to a client.\n\nRules:\n- List exactly the missing items given. Do not add any.\n- Under 110 words.\n- Nudge 1: friendly. Nudge 2: clear and helpful, say they can reply with photos or PDFs. Nudge 3 or more: direct, mention the needed-by date.\n- Only mention a consequence if one is given: {{ $json.consequence }}\nReturn only JSON with the keys \"subject\" and \"body\"."
            }
          ]
        }
      },
      "id": "79d7dc91-ad01-4d91-a470-fbe4f17fee13",
      "name": "Write the nudge",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        1040,
        0
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "gpt-5-mini",
          "mode": "list",
          "cachedResultName": "gpt-5-mini"
        },
        "options": {}
      },
      "id": "dd363819-84fd-4e6a-a2d6-1723b5c80249",
      "name": "Write the nudge model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [
        1040,
        266
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const raw = String($json.text ?? '').trim().replace(/^```(?:json)?/i, '').replace(/```$/, '').trim();\nlet out = {};\nlet ok = true;\ntry { out = JSON.parse(raw); } catch (e) { ok = false; }\nconst needs = [\"subject\",\"body\"];\nif (ok && !needs.every((k) => out[k] !== undefined && out[k] !== null)) ok = false;\nconst base = $('Pick clients to nudge').item.json;\n\nif (!ok) {\n  out = { ...out, subject: out.subject || 'Check this one: the AI reply could not be read', body: out.body || ('Original AI output:\\n' + raw) };\n}\nreturn { json: { ...base, ...out, canSend: ok } };"
      },
      "id": "8fe8f429-7453-42b5-a7e4-7b812e9003d6",
      "name": "Read the reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1300,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "296a24c0-0721-43f0-a7c9-03b12d11db68",
              "leftValue": "={{ $json.canSend && $('Settings').first().json.autoSend }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "86058cf5-dd12-466e-a95b-7aafd32a75c8",
      "name": "Auto-send on?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1560,
        0
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "send",
        "sendTo": "={{ $json.email }}",
        "subject": "={{ $json.subject }}",
        "emailType": "text",
        "message": "={{ $json.body }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "3fbbecf3-adf3-49ae-aacb-dc4c02c11d24",
      "name": "Send email",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1820,
        -85.5
      ]
    },
    {
      "parameters": {
        "resource": "draft",
        "operation": "create",
        "subject": "={{ $json.subject }}",
        "emailType": "text",
        "message": "={{ $json.body }}",
        "options": {
          "sendTo": "={{ $json.email }}"
        }
      },
      "id": "13c78add-c30d-4e5b-a6e6-ffa41a0b8b00",
      "name": "Save as Gmail draft",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1820,
        85.5
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "row_number": "={{ $('Pick clients to nudge').item.json.row_number }}",
            "Nudges Sent": "={{ $('Pick clients to nudge').item.json.nudge }}",
            "Last Nudge": "={{ $now.toFormat('yyyy-LL-dd') }}"
          },
          "matchingColumns": [
            "row_number"
          ],
          "schema": []
        },
        "options": {}
      },
      "id": "c31edca2-14bc-4dbc-a58f-308c665d3816",
      "name": "Mark nudged",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2080,
        0
      ]
    }
  ],
  "connections": {
    "Write the nudge model": {
      "ai_languageModel": [
        [
          {
            "node": "Write the nudge",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Read the reply": {
      "main": [
        [
          {
            "node": "Auto-send on?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Auto-send on?": {
      "main": [
        [
          {
            "node": "Send email",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Save as Gmail draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every weekday at 9am": {
      "main": [
        [
          {
            "node": "Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Settings": {
      "main": [
        [
          {
            "node": "Read requests",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read requests": {
      "main": [
        [
          {
            "node": "Pick clients to nudge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pick clients to nudge": {
      "main": [
        [
          {
            "node": "Write the nudge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write the nudge": {
      "main": [
        [
          {
            "node": "Read the reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send email": {
      "main": [
        [
          {
            "node": "Mark nudged",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save as Gmail draft": {
      "main": [
        [
          {
            "node": "Mark nudged",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "tags": []
}
