{
  "name": "Add only compliant leads from Google Sheets to Instantly with Precleared",
  "nodes": [
    {
      "parameters": {
        "content": "## Add only compliant leads from Google Sheets to Instantly with Precleared\n\nChecks every new lead in your Google Sheet with [Precleared](https://precleared.dev) before it goes anywhere near Instantly. Leads that are ok to cold email are added to your Instantly campaign; the rest stay out, with the reason and the next step on their row. Covers UK PECR and GDPR, US CAN-SPAM, Canada's CASL, Germany's UWG and French rules.\n\n### Who's it for\nAgencies and founders who build lead lists in Google Sheets and send cold email from Instantly.\n\n### How it works\n1. **Every morning** reads your sheet and keeps the rows that have no verdict yet.\n2. **Check with Precleared** asks whether a cold email to each address is allowed.\n3. **Cleared to email?** sends only `ok_to_email` leads on to **Add to Instantly**, with the receipt id as the custom variable `precleared_receipt`.\n4. **Write the result back** records the verdict, the rule, the next step, what the footer must include, the receipt id and whether the lead was added.\n\nA row Precleared couldn't answer (for example, once the day's checks are used) stays blank, with the reason in `note`, and is tried again on the next run. A row marked `error` (for example, an address that isn't valid) isn't retried: fix the row and clear its verdict.\n\n### How to set up\n1. Give your sheet these column headers: `email`, `first_name`, `last_name`, `company_name`, `address_source`, `company_number`, `recipient_country`, `verdict`, `rule`, `next_step`, `footer_requirement`, `receipt_id`, `checked_at`, `added_to_campaign`, `note`. In `address_source`, say where each address came from: published, bought, scraped, given or unknown (blank counts as unknown). `company_number` (a UK company number) and `recipient_country` (a two-letter code) are optional.\n2. Pick that sheet in **Read leads** and in **Write the result back**.\n3. Create a Header Auth credential for **Check with Precleared**: name `Authorization`, value `Bearer pc_live_...`. Get a free key at [precleared.dev/get-key](https://precleared.dev/get-key).\n4. Create a Header Auth credential for **Add to Instantly**: name `Authorization`, value `Bearer` followed by your Instantly API v2 key. The key needs the `leads:create` scope.\n5. Fill in **Your settings**: your country (GB or US), what you offer and your Instantly campaign id.\n6. Run it once, check the sheet and Instantly, then switch it on.\n\n### Requirements\n- A Google account for Sheets and an Instantly account with API access.\n- A Precleared key. A free key covers 25 checks a day; a £39 pack covers 2,000 and never expires.\n\n### How to customize\n- Run it when a row is added instead of every morning.\n- Set `max_checks_per_run` to how many leads you want checked each morning: up to 25 on a free key.\n- For a lead marked `unknown`, add its `company_number` or `recipient_country` and clear the verdict to check it again.\n- Make sure your Instantly emails meet what `footer_requirement` describes, usually who you are, a postal address and a simple way to opt out. It describes the requirement; it isn't wording to paste.\n- Change `skip_if_in_workspace` in **Add to Instantly** if you want leads that are already in another campaign to be added again.\n\nPrecleared is decision support with sources, not legal advice.",
        "height": 1080,
        "width": 600
      },
      "id": "33ea9ce7-7276-5fc0-93ae-d803777734fb",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1020,
        -560
      ]
    },
    {
      "parameters": {
        "content": "## 1. Read new leads\nEvery morning, read the sheet and keep the rows with no verdict yet.",
        "height": 420,
        "width": 880,
        "color": 7
      },
      "id": "18effc89-113a-5971-9fc2-cd107f2eac29",
      "name": "Section: read",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -380,
        -240
      ]
    },
    {
      "parameters": {
        "content": "## 2. Check each address\nUp to `max_checks_per_run` leads a run, one Precleared check each.",
        "height": 420,
        "width": 680,
        "color": 7
      },
      "id": "dfb52c51-8a0a-57f8-a130-736da7b8cdad",
      "name": "Section: check",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        540,
        -240
      ]
    },
    {
      "parameters": {
        "content": "## 3. Add cleared leads to Instantly\nOnly `ok_to_email` leads go on. Everything else skips straight to the sheet.",
        "height": 620,
        "width": 700,
        "color": 7
      },
      "id": "1207e1ff-ad8d-5ac2-a9ac-5eb011bf5f04",
      "name": "Section: hand off",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1240,
        -440
      ]
    },
    {
      "parameters": {
        "content": "## 4. Write the result back\nVerdict, rule, next step, footer requirement, receipt id and whether the lead was added.",
        "height": 420,
        "width": 440,
        "color": 7
      },
      "id": "8c5e3a8b-1a87-5914-ae6f-e96b8b096513",
      "name": "Section: write back",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1980,
        -240
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "days",
              "triggerAtHour": 9
            }
          ]
        }
      },
      "id": "0b0c2c8b-e1e2-57a5-a036-f10ed74e562e",
      "name": "Every morning",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -300,
        0
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f3973394-50ea-5475-b5d6-2704b3033a29",
              "name": "sender_country",
              "value": "GB",
              "type": "string"
            },
            {
              "id": "b2e8454c-b2be-5bad-8e72-bc4aaef91c75",
              "name": "pitch",
              "value": "what you offer, in a few words",
              "type": "string"
            },
            {
              "id": "c79940ae-efb0-53de-acae-3d04e47f9d04",
              "name": "max_checks_per_run",
              "value": 25,
              "type": "number"
            },
            {
              "id": "0218d189-15d7-5aaa-a3dd-1d6eb686f264",
              "name": "instantly_campaign_id",
              "value": "paste your Instantly campaign id",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "5f0d3736-bc8b-5d86-ada5-88f35a29c7f2",
      "name": "Your settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -80,
        0
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": ""
        },
        "sheetName": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": ""
        },
        "options": {}
      },
      "id": "4be21259-26ce-53d0-82bf-fb0de08e550a",
      "name": "Read leads",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        140,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "18af9e7a-f467-548f-87d4-311ea972f346",
              "leftValue": "={{ $json.email }}",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "rightValue": ""
            },
            {
              "id": "50e57ad2-88fb-584f-a195-fc54b3f1374f",
              "leftValue": "={{ $json.verdict }}",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "rightValue": ""
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "04838512-df09-57c8-90e0-f33aed9a0c55",
      "name": "Not checked yet",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        360,
        0
      ]
    },
    {
      "parameters": {
        "maxItems": "={{ $('Your settings').first().json.max_checks_per_run }}"
      },
      "id": "4b57a68f-62f6-5086-abd8-887fd405c8c2",
      "name": "Stay within your allowance",
      "type": "n8n-nodes-base.limit",
      "typeVersion": 1,
      "position": [
        620,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://precleared.dev/api/v1/check",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ recipient_email: $json.email, address_source: $json.address_source || 'unknown', sender_country: $('Your settings').first().json.sender_country, pitch: $('Your settings').first().json.pitch, company_number: $json.company_number || undefined, recipient_country: $json.recipient_country || undefined }) }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 5,
              "batchInterval": 1000
            }
          },
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "8b7c0ff3-d31e-589c-bddc-7029787474d4",
      "name": "Check with Precleared",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        840,
        0
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "75dd85bd-975c-53f1-96b6-f003923ecf46",
              "name": "email",
              "value": "={{ $('Stay within your allowance').item.json.email }}",
              "type": "string"
            },
            {
              "id": "e309c801-8a3f-503f-b655-42403d53cb89",
              "name": "verdict",
              "value": "={{ $json.verdict || (['daily_limit', 'payment_required'].includes($json.error) || !$json.error ? '' : 'error') }}",
              "type": "string"
            },
            {
              "id": "9cafd26b-565c-5181-ba25-010fa2e61da8",
              "name": "rule",
              "value": "={{ ($json.reasons && $json.reasons[0] && ($json.reasons[0].text || $json.reasons[0].note)) || '' }}",
              "type": "string"
            },
            {
              "id": "d025016a-8f11-5cf5-9edb-f89881a24842",
              "name": "next_step",
              "value": "={{ $json.next_step || '' }}",
              "type": "string"
            },
            {
              "id": "7a8aceca-71ef-54ed-8dcc-1e563b00248a",
              "name": "footer_requirement",
              "value": "={{ $json.footer_text || '' }}",
              "type": "string"
            },
            {
              "id": "dcffe83f-123d-5f3b-b0ab-eef44b04d704",
              "name": "receipt_id",
              "value": "={{ $json.receipt ? $json.receipt.id : '' }}",
              "type": "string"
            },
            {
              "id": "b0d1bd39-b171-55ff-918f-0b7b3f2033cc",
              "name": "checked_at",
              "value": "={{ $json.verdict ? $now.toISO() : '' }}",
              "type": "string"
            },
            {
              "id": "16a9ddea-29bc-5849-947d-5ac9cc83fa0e",
              "name": "added_to_campaign",
              "value": "={{ $json.verdict && $json.verdict !== 'ok_to_email' ? 'no: not cleared' : '' }}",
              "type": "string"
            },
            {
              "id": "55bb2cc0-f909-5ec2-b284-9a27a7d745e5",
              "name": "note",
              "value": "={{ $json.verdict ? '' : [($json.message || (typeof $json.error === 'string' ? $json.error : '') || 'Precleared did not answer'), ($json.get_key_url ? 'Free key: ' + $json.get_key_url : ''), ($json.buy_url ? 'Packs: ' + $json.buy_url : '')].filter(Boolean).join(' ') }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "6d17bd99-8de3-517e-b93c-570bc17fd367",
      "name": "Prepare the update",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1060,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "0ad9c839-6b89-5d6b-a466-67553a2478b8",
              "leftValue": "={{ $json.verdict }}",
              "rightValue": "ok_to_email",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "3d3def4f-be73-5893-aea6-af88947d9e8f",
      "name": "Cleared to email?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1300,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.instantly.ai/api/v2/leads/add",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ campaign_id: $('Your settings').first().json.instantly_campaign_id, skip_if_in_workspace: true, leads: [{ email: $json.email, first_name: $('Stay within your allowance').item.json.first_name || null, last_name: $('Stay within your allowance').item.json.last_name || null, company_name: $('Stay within your allowance').item.json.company_name || null, custom_variables: { precleared_receipt: $json.receipt_id } }] }) }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "ba0fdb7a-f971-5caf-86eb-889f5b589d49",
      "name": "Add to Instantly",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1520,
        -200
      ],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "89ae57dd-1fdc-5741-9279-7e35915ce18f",
              "name": "email",
              "value": "={{ $('Prepare the update').item.json.email }}",
              "type": "string"
            },
            {
              "id": "645b6bde-a064-5adf-85b1-dc5566e33928",
              "name": "verdict",
              "value": "={{ $('Prepare the update').item.json.verdict }}",
              "type": "string"
            },
            {
              "id": "3a6519cd-7a3c-5253-a679-1ce7a441da1d",
              "name": "rule",
              "value": "={{ $('Prepare the update').item.json.rule }}",
              "type": "string"
            },
            {
              "id": "71e51f5b-08f2-54ed-a58a-bb522310f87a",
              "name": "next_step",
              "value": "={{ $('Prepare the update').item.json.next_step }}",
              "type": "string"
            },
            {
              "id": "99c5d40d-b965-5dbd-9a02-0ae45b00896d",
              "name": "footer_requirement",
              "value": "={{ $('Prepare the update').item.json.footer_requirement }}",
              "type": "string"
            },
            {
              "id": "f26fb336-5c4a-5a2d-a804-b2dd5d291a8d",
              "name": "receipt_id",
              "value": "={{ $('Prepare the update').item.json.receipt_id }}",
              "type": "string"
            },
            {
              "id": "9f5150e0-f647-5298-8fa7-eadbdbdb0281",
              "name": "checked_at",
              "value": "={{ $('Prepare the update').item.json.checked_at }}",
              "type": "string"
            },
            {
              "id": "16a9ddea-29bc-5849-947d-5ac9cc83fa0e",
              "name": "added_to_campaign",
              "value": "={{ ($json.error || ($json.statusCode && $json.statusCode >= 400)) ? 'failed: ' + ($json.message || $json.error) : ((typeof $json.leads_uploaded === 'number' ? $json.leads_uploaded === 0 : (($json.duplicated_leads || 0) + ($json.skipped_count || 0) + ($json.in_blocklist || 0) + ($json.invalid_email_count || 0) + ($json.incomplete_count || 0)) > 0) ? 'skipped: Instantly did not add it (already in the workspace, on a blocklist or invalid)' : 'added') }}",
              "type": "string"
            },
            {
              "id": "1349d47b-66a5-5b23-8f0a-47b3a2568e26",
              "name": "note",
              "value": "={{ $('Prepare the update').item.json.note }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "0f003811-33f8-5205-97c1-7d9c5f50bcfe",
      "name": "Note the hand-off",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1740,
        -200
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": ""
        },
        "sheetName": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": ""
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "email": "={{ $json.email }}",
            "verdict": "={{ $json.verdict }}",
            "rule": "={{ $json.rule }}",
            "next_step": "={{ $json.next_step }}",
            "footer_requirement": "={{ $json.footer_requirement }}",
            "receipt_id": "={{ $json.receipt_id }}",
            "checked_at": "={{ $json.checked_at }}",
            "added_to_campaign": "={{ $json.added_to_campaign }}",
            "note": "={{ $json.note }}"
          },
          "matchingColumns": [
            "email"
          ],
          "schema": [
            {
              "id": "email",
              "displayName": "email",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "first_name",
              "displayName": "first_name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "last_name",
              "displayName": "last_name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "company_name",
              "displayName": "company_name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "address_source",
              "displayName": "address_source",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "company_number",
              "displayName": "company_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "recipient_country",
              "displayName": "recipient_country",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "verdict",
              "displayName": "verdict",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "rule",
              "displayName": "rule",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "next_step",
              "displayName": "next_step",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "footer_requirement",
              "displayName": "footer_requirement",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "receipt_id",
              "displayName": "receipt_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "checked_at",
              "displayName": "checked_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "added_to_campaign",
              "displayName": "added_to_campaign",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "note",
              "displayName": "note",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "c1547c30-7381-55f4-ac90-cb7ff54caf0d",
      "name": "Write the result back",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2080,
        0
      ]
    }
  ],
  "pinData": {},
  "connections": {
    "Every morning": {
      "main": [
        [
          {
            "node": "Your settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Your settings": {
      "main": [
        [
          {
            "node": "Read leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read leads": {
      "main": [
        [
          {
            "node": "Not checked yet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Not checked yet": {
      "main": [
        [
          {
            "node": "Stay within your allowance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stay within your allowance": {
      "main": [
        [
          {
            "node": "Check with Precleared",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check with Precleared": {
      "main": [
        [
          {
            "node": "Prepare the update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare the update": {
      "main": [
        [
          {
            "node": "Cleared to email?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cleared to email?": {
      "main": [
        [
          {
            "node": "Add to Instantly",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Write the result back",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Note the hand-off": {
      "main": [
        [
          {
            "node": "Write the result back",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add to Instantly": {
      "main": [
        [
          {
            "node": "Note the hand-off",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
