← All CRM

Drift email verification with BillionVerify

Drift is a conversational marketing and sales CRM platform that captures leads through live chat, bots, and meeting scheduling. Integrating BillionVerify ensures that email addresses collected during Drift conversations are real and deliverable before they enter your sales pipeline or trigger automated follow-ups.

Why verify before the send

Leads that give fake or disposable email addresses in Drift conversations waste sales rep time and skew funnel metrics. BillionVerify validates every email at the moment of capture, so your CRM stays clean, follow-up sequences reach real buyers, and your revenue team focuses on genuine opportunities.

Ready-to-use n8n workflow

Import this workflow into n8n β€” it verifies every address with BillionVerify before Drift sends, so only deliverable contacts are emailed. Install the BillionVerify community node first, then add your API key. Adapted from this n8n template

verify-emails-in-drift.json
{
  "name": "Track commodity portfolio drift with Google Sheets, Gemini AI and Gmail alerts + BillionVerify",
  "nodes": [
    {
      "id": "7292b81c-f3fb-4b68-abf6-f3a66e703167",
      "name": "Overview Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3424,
        3728
      ],
      "parameters": {
        "width": 1102,
        "height": 316,
        "content": "## Commodity Portfolio Tracker\n\n**How it works**  \nThis workflow runs on a schedule, reads your holdings from Google Sheets, loads target allocation rules from the workflow config node, validates the data, calculates actual allocation, detects drift against your allowed range, classifies the alert severity, generates a plain-text rebalance message with Gemini, emails the result and logs the workflow outcome.\n\n**Setup steps**  \n1. Connect Google Sheets, Gmail and Gemini credentials.  \n2. Confirm the holdings sheet and spreadsheet are correct.  \n3. Create a `rebalance_log` sheet with log columns.  \n4. Update target allocation, alert email and thresholds in the config node.  \n5. Test the workflow once manually, then activate the schedule."
      },
      "typeVersion": 1
    },
    {
      "id": "3a83644b-da4f-4f5c-b6be-d51da74b4af3",
      "name": "Input and Settings Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3424,
        4112
      ],
      "parameters": {
        "color": 7,
        "width": 932,
        "height": 424,
        "content": "## Input and Settings\n\nThis section pulls the latest holdings from Google Sheets and loads the portfolio rules used in the workflow. The settings node acts like the control panel, where you can update target allocation, allowed ranges, alert email, currency and severity rules without changing the rest of the flow."
      },
      "typeVersion": 1
    },
    {
      "id": "233431cb-acd0-41bb-8fe6-9410238a2bf6",
      "name": "Analysis and Decision Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4416,
        4096
      ],
      "parameters": {
        "color": 7,
        "width": 1076,
        "height": 584,
        "content": "## Analysis and Decision\n\nThis part checks whether the input data is clean and usable before doing any calculations. It then works out the actual allocation for each asset, compares it with the target range, identifies which assets are overweight or underweight and decides whether a rebalance alert is needed."
      },
      "typeVersion": 1
    },
    {
      "id": "8527d92a-918b-4e5c-af4f-86b0f81508c6",
      "name": "Alert Message and Logging Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5552,
        3904
      ],
      "parameters": {
        "color": 7,
        "width": 1304,
        "height": 616,
        "content": "## Alert Message and Logging\n\nOnce drift is found, this section turns the result into a clear alert message. Gemini helps format the recommendation in simple language, the email node sends it to the selected recipient and the final step stores the outcome in the log sheet so you have a record of what happened in each run."
      },
      "typeVersion": 1
    },
    {
      "id": "7dd2c230-3740-4a49-a914-026636a254eb",
      "name": "Read Holdings",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3760,
        4384
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_lcrJ7b0c9ZqyenXZZZ-A5Yyb99lS-k8qSs-Y9PV3EA/edit#gid=0",
          "cachedResultName": "holdings"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_lcrJ7b0c9ZqyenXZZZ-A5Yyb99lS-k8qSs-Y9PV3EA",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_lcrJ7b0c9ZqyenXZZZ-A5Yyb99lS-k8qSs-Y9PV3EA/edit?usp=drivesdk",
          "cachedResultName": "Commodity Portfolio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credential-id",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "79ce3c3c-442a-4a68-af7d-1a7eac2e6039",
      "name": "Workflow Settings",
      "type": "n8n-nodes-base.set",
      "position": [
        3760,
        4224
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"targets\": [\n    { \"asset\": \"Gold\", \"target_pct\": 40, \"min_pct\": 35, \"max_pct\": 45 },\n    { \"asset\": \"Silver\", \"target_pct\": 20, \"min_pct\": 15, \"max_pct\": 25 },\n    { \"asset\": \"Oil\", \"target_pct\": 20, \"min_pct\": 15, \"max_pct\": 25 },\n    { \"asset\": \"Copper\", \"target_pct\": 10, \"min_pct\": 5, \"max_pct\": 15 },\n    { \"asset\": \"Natural Gas\", \"target_pct\": 10, \"min_pct\": 5, \"max_pct\": 15 }\n  ],\n  \"threshold_pct\": 5,\n  \"cooldown_days\": 3,\n  \"alert_email\": \"user@example.com\",\n  \"email_enabled\": \"yes\",\n  \"ai_enabled\": \"yes\",\n  \"portfolio_name\": \"Commodity Portfolio\",\n  \"currency\": \"INR\",\n  \"rebalance_mode\": \"amount\",\n  \"severity_rules\": {\n    \"low_max\": 5,\n    \"medium_max\": 10\n  }\n}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "7d538dfb-8097-4512-b38a-80bd479d671a",
      "name": "Synchronize Inputs",
      "type": "n8n-nodes-base.merge",
      "position": [
        4048,
        4352
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "c0746678-d2e6-4459-8365-9c20c86a7373",
      "name": "Prepare Portfolio Context",
      "type": "n8n-nodes-base.code",
      "position": [
        4240,
        4352
      ],
      "parameters": {
        "jsCode": "const holdings = $('Read Holdings').all().map(i => i.json);\nconst config = $('Workflow Settings').first().json;\n\nreturn [\n  {\n    json: {\n      holdings,\n      config\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "91c40090-c446-45d2-ac33-f9e4c3bec82c",
      "name": "Validate Portfolio Data",
      "type": "n8n-nodes-base.code",
      "position": [
        4464,
        4352
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst holdings = data.holdings || [];\nconst config = data.config || {};\nconst targets = config.targets || [];\n\nif (!holdings.length) {\n  return [{\n    json: {\n      valid: false,\n      error: 'No holdings found.',\n      holdings,\n      config\n    }\n  }];\n}\n\nif (!targets.length) {\n  return [{\n    json: {\n      valid: false,\n      error: 'Targets are missing in workflow settings.',\n      holdings,\n      config\n    }\n  }];\n}\n\nconst targetTotal = targets.reduce((sum, t) => sum + Number(t.target_pct || 0), 0);\nif (targetTotal !== 100) {\n  return [{\n    json: {\n      valid: false,\n      error: `Target allocation must total 100. Current total is ${targetTotal}.`,\n      holdings,\n      config\n    }\n  }];\n}\n\nconst seenAssets = new Set();\n\nfor (const h of holdings) {\n  const asset = String(h.asset || '').trim();\n  const units = Number(h.units);\n  const price = Number(h.price);\n\n  if (!asset) {\n    return [{\n      json: {\n        valid: false,\n        error: 'One holding has a blank asset name.',\n        holdings,\n        config\n      }\n    }];\n  }\n\n  if (seenAssets.has(asset)) {\n    return [{\n      json: {\n        valid: false,\n        error: `Duplicate asset found in holdings: ${asset}`,\n        holdings,\n        config\n      }\n    }];\n  }\n  seenAssets.add(asset);\n\n  if (Number.isNaN(units) || units < 0) {\n    return [{\n      json: {\n        valid: false,\n        error: `Invalid units for asset: ${asset}`,\n        holdings,\n        config\n      }\n    }];\n  }\n\n  if (Number.isNaN(price) || price < 0) {\n    return [{\n      json: {\n        valid: false,\n        error: `Invalid price for asset: ${asset}`,\n        holdings,\n        config\n      }\n    }];\n  }\n\n  const target = targets.find(t => String(t.asset || '').trim() === asset);\n  if (!target) {\n    return [{\n      json: {\n        valid: false,\n        error: `No target config found for asset: ${asset}`,\n        holdings,\n        config\n      }\n    }];\n  }\n}\n\nreturn [{\n  json: {\n    valid: true,\n    error: null,\n    holdings,\n    config\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f66778ac-7cb4-4ce7-b4cc-81e08796c440",
      "name": "Check Validation Status",
      "type": "n8n-nodes-base.if",
      "position": [
        4704,
        4352
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.valid }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "52d2ef3f-d231-49b2-b7b2-e134326fe6a4",
      "name": "Calculate Portfolio Allocation",
      "type": "n8n-nodes-base.code",
      "position": [
        4928,
        4240
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst holdings = data.holdings;\nconst config = data.config;\n\nconst enrichedHoldings = holdings.map(h => {\n  const units = Number(h.units);\n  const price = Number(h.price);\n  const current_value = Number(h.current_value ?? (units * price));\n\n  return {\n    row_number: h.row_number,\n    asset: h.asset,\n    units,\n    price,\n    current_value,\n    last_updated: h.last_updated\n  };\n});\n\nconst total_value = enrichedHoldings.reduce((sum, h) => sum + h.current_value, 0);\n\nconst assets = enrichedHoldings.map(h => {\n  const target = config.targets.find(t => t.asset === h.asset);\n\n  if (!target) {\n    throw new Error(`Missing target config for asset: ${h.asset}`);\n  }\n\n  const actual_pct = total_value === 0 ? 0 : (h.current_value / total_value) * 100;\n\n  return {\n    ...h,\n    actual_pct,\n    target_pct: Number(target.target_pct),\n    min_pct: Number(target.min_pct),\n    max_pct: Number(target.max_pct)\n  };\n});\n\nreturn [{\n  json: {\n    holdings: assets,\n    total_value,\n    config\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "15978e61-2607-442d-9004-63d5aa829bb8",
      "name": "Detect Portfolio Drift",
      "type": "n8n-nodes-base.code",
      "position": [
        5136,
        4240
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst holdings = data.holdings;\nconst total_value = data.total_value;\nconst config = data.config;\n\nlet rebalance_needed = false;\n\nconst assets = holdings.map(h => {\n  const deviation_pct = h.actual_pct - h.target_pct;\n  const target_value = total_value * (h.target_pct / 100);\n  const adjustment_amount = Math.abs(h.current_value - target_value);\n\n  let status = 'within_range';\n  let suggested_action = 'Hold';\n\n  if (h.actual_pct > h.max_pct) {\n    status = 'overweight';\n    suggested_action = 'Reduce';\n    rebalance_needed = true;\n  } else if (h.actual_pct < h.min_pct) {\n    status = 'underweight';\n    suggested_action = 'Increase';\n    rebalance_needed = true;\n  }\n\n  return {\n    ...h,\n    deviation_pct,\n    target_value,\n    adjustment_amount,\n    status,\n    suggested_action\n  };\n});\n\nconst affected_assets = assets\n  .filter(a => a.status !== 'within_range')\n  .map(a => a.asset);\n\nreturn [{\n  json: {\n    total_value,\n    assets,\n    affected_assets,\n    rebalance_needed,\n    config\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "3d0e7dce-1d90-44c7-8751-5c83c27b46bb",
      "name": "Check Rebalance Requirement",
      "type": "n8n-nodes-base.if",
      "position": [
        5360,
        4240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.rebalance_needed }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "ec658cb8-383f-43c4-8c95-3717f711a94e",
      "name": "Classify Alert Severity",
      "type": "n8n-nodes-base.code",
      "position": [
        5584,
        4144
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst assets = data.assets;\nconst config = data.config;\n\nconst maxDeviation = Math.max(...assets.map(a => Math.abs(a.deviation_pct)));\n\nlet severity = 'low';\nif (maxDeviation > Number(config.severity_rules.medium_max)) {\n  severity = 'high';\n} else if (maxDeviation > Number(config.severity_rules.low_max)) {\n  severity = 'medium';\n}\n\nreturn [{\n  json: {\n    ...data,\n    severity,\n    max_deviation: maxDeviation\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "622f0d6d-17b4-4ec0-ab5f-1f67ddd90b95",
      "name": "Build Alert Prompt",
      "type": "n8n-nodes-base.code",
      "position": [
        5808,
        4144
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\nconst affected = data.assets\n  .filter(a => a.status !== 'within_range')\n  .map(a => `${a.asset}: ${a.status}, actual ${a.actual_pct.toFixed(2)}%, target ${a.target_pct.toFixed(2)}%, deviation ${a.deviation_pct.toFixed(2)}%, action ${a.suggested_action}, amount ${data.config.currency} ${a.adjustment_amount.toFixed(2)}`)\n  .join('\\n');\n\nconst ai_input = `\nYou are a financial portfolio monitoring assistant.\n\nYour task is to convert structured portfolio rebalance data into a professional alert message.\n\nSTRICT RULES:\n- Use only the numbers provided below\n- Do NOT recalculate anything\n- Do NOT invent any values\n- Keep the message concise, clear and professional\n- Use plain text only\n- Mention severity naturally\n\nDATA:\nPortfolio Name: ${data.config.portfolio_name}\nCurrency: ${data.config.currency}\nTotal Portfolio Value: ${data.total_value}\nSeverity: ${data.severity}\nMaximum Deviation: ${data.max_deviation.toFixed(2)}%\n\nAssets requiring rebalance:\n${affected}\n\nOUTPUT FORMAT:\nWrite a clean paragraph-style alert message suitable for email.\n`;\n\nreturn [{\n  json: {\n    ...data,\n    ai_input\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "9379e3af-ce16-481c-9def-ca9541d41667",
      "name": "Generate Alert Message",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        5952,
        4064
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-3.1-flash-lite-preview",
          "cachedResultName": "models/gemini-3.1-flash-lite-preview"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "={{ $json.ai_input }}"
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "googlePalmApi": {
          "id": "credential-id",
          "name": "Google Gemini(PaLM) Api account"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "77a77ce0-4569-4c61-bb8c-cf7a1d885dcd",
      "name": "Merge Alert Data",
      "type": "n8n-nodes-base.merge",
      "position": [
        6256,
        4128
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "a8d39754-f8b5-4cab-a8f2-819ceec55b46",
      "name": "Prepare Alert Payload",
      "type": "n8n-nodes-base.set",
      "position": [
        6432,
        4128
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "3de685c8-ff1f-43a1-870d-e58542b7596b",
              "name": "alert_message",
              "type": "string",
              "value": "={{ $json.content.parts[0].text }}"
            },
            {
              "id": "fbc8f85a-24b4-40d0-9981-e4b85f38c6a1",
              "name": "email_to",
              "type": "string",
              "value": "={{ $json.config.alert_email }}"
            },
            {
              "id": "dc6b09bd-6745-4110-8ec4-2322014da2a1",
              "name": "email_subject",
              "type": "string",
              "value": "=Commodity Portfolio Alert - {{$json.severity.toUpperCase()}}"
            },
            {
              "id": "c715d84c-f0c7-47c5-a3ef-2dd0c2bccbd6",
              "name": "affected_assets_text",
              "type": "string",
              "value": "={{ $json.affected_assets.join(', ') }}"
            },
            {
              "id": "f4861430-940f-48b2-be93-2d810c5ada97",
              "name": "run_date",
              "type": "string",
              "value": "={{ $now }}"
            },
            {
              "id": "aef321d5-2581-43e2-85db-42cdb7515f16",
              "name": "alert_sent",
              "type": "string",
              "value": "yes"
            },
            {
              "id": "43b07038-8485-4efa-9c06-7d0eee590f5e",
              "name": "total_value",
              "type": "number",
              "value": "={{ $json.total_value }}"
            },
            {
              "id": "24388c93-02d0-4fa8-b84f-83a7f1fca40b",
              "name": "severity",
              "type": "string",
              "value": "={{ $json.severity }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "708ee600-4e9a-4265-bbf1-3c105dbf080f",
      "name": "Send Rebalance Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        6608,
        4048
      ],
      "webhookId": "6221c310-de84-4f47-bce1-c66d8565e0d7",
      "parameters": {
        "sendTo": "={{ $('Prepare Alert Payload').item.json.email_to }}",
        "message": "={{ $('Prepare Alert Payload').item.json.alert_message + '\\n\\nAffected Assets: ' + $('Prepare Alert Payload').item.json.affected_assets_text + '\\nSeverity: ' + $('Prepare Alert Payload').item.json.severity + '\\nTotal Portfolio Value: ' + $('Prepare Alert Payload').item.json.total_value + '\\nRun Date: ' + $('Prepare Alert Payload').item.json.run_date }}",
        "options": {},
        "subject": "={{ $('Prepare Alert Payload').item.json.email_subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "credential-id",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f6e4ac7b-b17d-4d3e-91f4-4656880e1a4a",
      "name": "Log Sent Alert",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        6608,
        4224
      ],
      "parameters": {
        "columns": {
          "value": {
            "summary": "={{ $json.alert_message }}",
            "run_date": "={{ $json.run_date }}",
            "severity": "={{ $json.severity }}",
            "alert_sent": "={{ $json.alert_sent }}",
            "total_value": "={{ $json.total_value }}",
            "affected_assets": "={{ $json.affected_assets_text }}",
            "rebalance_needed": "yes"
          },
          "schema": [
            {
              "id": "run_date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "run_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total_value",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "total_value",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "rebalance_needed",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "rebalance_needed",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "severity",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "severity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "affected_assets",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "affected_assets",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "alert_sent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "alert_sent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reason_skipped",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "reason_skipped",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 347522276,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_lcrJ7b0c9ZqyenXZZZ-A5Yyb99lS-k8qSs-Y9PV3EA/edit#gid=347522276",
          "cachedResultName": "rebalance_log"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_lcrJ7b0c9ZqyenXZZZ-A5Yyb99lS-k8qSs-Y9PV3EA",
          "cachedResultName": "Commodity Portfolio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credential-id",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "91883dee-8be2-4ed6-93a8-802dda291517",
      "name": "Prepare Validation Failure Log",
      "type": "n8n-nodes-base.set",
      "position": [
        4928,
        4512
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "name": "run_date",
              "type": "string",
              "value": "={{ $now }}"
            },
            {
              "name": "total_value",
              "type": "string",
              "value": ""
            },
            {
              "name": "rebalance_needed",
              "type": "string",
              "value": "no"
            },
            {
              "name": "severity",
              "type": "string",
              "value": "validation_failed"
            },
            {
              "name": "affected_assets",
              "type": "string",
              "value": ""
            },
            {
              "name": "alert_sent",
              "type": "string",
              "value": "no"
            },
            {
              "name": "summary",
              "type": "string",
              "value": "Validation failed before allocation analysis."
            },
            {
              "name": "reason_skipped",
              "type": "string",
              "value": "={{ $json.error }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "5300f585-5b9a-42c8-88ca-b3cb8766ac80",
      "name": "Log Validation Failure",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        5136,
        4512
      ],
      "parameters": {
        "columns": {
          "value": {
            "summary": "={{ $json.summary }}",
            "run_date": "={{ $json.run_date }}",
            "severity": "={{ $json.severity }}",
            "alert_sent": "={{ $json.alert_sent }}",
            "total_value": "={{ $json.total_value }}",
            "reason_skipped": "={{ $json.reason_skipped }}",
            "affected_assets": "={{ $json.affected_assets }}",
            "rebalance_needed": "={{ $json.rebalance_needed }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 347522276,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_lcrJ7b0c9ZqyenXZZZ-A5Yyb99lS-k8qSs-Y9PV3EA/edit#gid=347522276",
          "cachedResultName": "rebalance_log"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_lcrJ7b0c9ZqyenXZZZ-A5Yyb99lS-k8qSs-Y9PV3EA",
          "cachedResultName": "Commodity Portfolio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credential-id",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "c899fa73-de9a-4814-ab75-71489c8da0d2",
      "name": "Prepare No Action Log",
      "type": "n8n-nodes-base.set",
      "position": [
        5584,
        4304
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "name": "run_date",
              "type": "string",
              "value": "={{ $now }}"
            },
            {
              "name": "total_value",
              "type": "string",
              "value": "={{ $json.total_value }}"
            },
            {
              "name": "rebalance_needed",
              "type": "string",
              "value": "no"
            },
            {
              "name": "severity",
              "type": "string",
              "value": "none"
            },
            {
              "name": "affected_assets",
              "type": "string",
              "value": ""
            },
            {
              "name": "alert_sent",
              "type": "string",
              "value": "no"
            },
            {
              "name": "summary",
              "type": "string",
              "value": "No drift detected. Portfolio is within target range."
            },
            {
              "name": "reason_skipped",
              "type": "string",
              "value": "No rebalance needed."
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e62048b2-5b87-4166-84cb-25109dcdfdfd",
      "name": "Log No Action",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        5808,
        4304
      ],
      "parameters": {
        "columns": {
          "value": {
            "summary": "={{ $json.summary }}",
            "run_date": "={{ $json.run_date }}",
            "severity": "={{ $json.severity }}",
            "alert_sent": "={{ $json.alert_sent }}",
            "total_value": "={{ $json.total_value }}",
            "reason_skipped": "={{ $json.reason_skipped }}",
            "affected_assets": "={{ $json.affected_assets }}",
            "rebalance_needed": "={{ $json.rebalance_needed }}"
          },
          "schema": [
            {
              "id": "run_date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "run_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total_value",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "total_value",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "rebalance_needed",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "rebalance_needed",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "severity",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "severity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "affected_assets",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "affected_assets",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "alert_sent",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "alert_sent",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reason_skipped",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "reason_skipped",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 347522276,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_lcrJ7b0c9ZqyenXZZZ-A5Yyb99lS-k8qSs-Y9PV3EA/edit#gid=347522276",
          "cachedResultName": "rebalance_log"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_lcrJ7b0c9ZqyenXZZZ-A5Yyb99lS-k8qSs-Y9PV3EA",
          "cachedResultName": "Commodity Portfolio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credential-id",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "0695d34f-1ed2-4f48-8d5b-07fd7ae07158",
      "name": "Daily Portfolio Rebalance Check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        3488,
        4352
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "parameters": {
        "operation": "verify",
        "email": "={{ $('Prepare Alert Payload').item.json.email_to }}",
        "additionalOptions": {}
      },
      "type": "n8n-nodes-billionverify.billionVerify",
      "typeVersion": 1,
      "position": [
        6248,
        4048
      ],
      "name": "Verify Email (BillionVerify)",
      "credentials": {
        "billionVerifyApi": {
          "id": "",
          "name": "BillionVerify account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "is-deliverable",
              "leftValue": "={{ $json.is_deliverable }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ]
        }
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        6428,
        4048
      ],
      "name": "IF deliverable"
    }
  ],
  "connections": {
    "Read Holdings": {
      "main": [
        [
          {
            "node": "Synchronize Inputs",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Log Sent Alert": {
      "main": [
        []
      ]
    },
    "Merge Alert Data": {
      "main": [
        [
          {
            "node": "Prepare Alert Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Settings": {
      "main": [
        [
          {
            "node": "Synchronize Inputs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Alert Prompt": {
      "main": [
        [
          {
            "node": "Generate Alert Message",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge Alert Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Synchronize Inputs": {
      "main": [
        [
          {
            "node": "Prepare Portfolio Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Rebalance Email": {
      "main": [
        []
      ]
    },
    "Prepare Alert Payload": {
      "main": [
        [
          {
            "node": "Log Sent Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Verify Email (BillionVerify)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare No Action Log": {
      "main": [
        [
          {
            "node": "Log No Action",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Detect Portfolio Drift": {
      "main": [
        [
          {
            "node": "Check Rebalance Requirement",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Alert Message": {
      "main": [
        [
          {
            "node": "Merge Alert Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Validation Status": {
      "main": [
        [
          {
            "node": "Calculate Portfolio Allocation",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Validation Failure Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Alert Severity": {
      "main": [
        [
          {
            "node": "Build Alert Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Portfolio Data": {
      "main": [
        [
          {
            "node": "Check Validation Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Portfolio Context": {
      "main": [
        [
          {
            "node": "Validate Portfolio Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Rebalance Requirement": {
      "main": [
        [
          {
            "node": "Classify Alert Severity",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare No Action Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Portfolio Allocation": {
      "main": [
        [
          {
            "node": "Detect Portfolio Drift",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Validation Failure Log": {
      "main": [
        [
          {
            "node": "Log Validation Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Portfolio Rebalance Check": {
      "main": [
        [
          {
            "node": "Read Holdings",
            "type": "main",
            "index": 0
          },
          {
            "node": "Workflow Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify Email (BillionVerify)": {
      "main": [
        [
          {
            "node": "IF deliverable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF deliverable": {
      "main": [
        [
          {
            "node": "Send Rebalance Email",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  }
}

Workflow templates with Drift

Ready-to-use workflows that verify emails before Drift sends.

How it works

  1. 1

    Set up a trigger in n8n or Integrately that fires when Drift captures a new email address.

  2. 2

    Forward the address to BillionVerify for real-time verification.

  3. 3

    Receive a verdict covering validity, disposable status, role-address detection, and catch-all risk.

  4. 4

    Route verified leads directly to your Drift CRM or sales sequences; quarantine or drop invalid ones.

  5. 5

    Enjoy a cleaner pipeline with follow-up emails that actually arrive.

When to use this

Validate emails captured by Drift chatbots

When a chatbot collects an email during a conversation, send it to BillionVerify before routing the lead to your CRM or sales sequence. Invalid and disposable addresses are caught immediately, keeping your pipeline filled with real prospects.

Protect meeting-booking flows

Before confirming a meeting booked through Drift, verify the provided email address. This ensures calendar invites and confirmation emails actually reach the prospect, reducing no-shows caused by incorrect contact details.

Clean imported contact lists

When loading external contact lists into Drift for targeted campaigns, run each address through BillionVerify first. Remove invalid and catch-all addresses to improve deliverability and keep your sender reputation healthy.

FAQ

Can BillionVerify work with Drift's chatbot flows in real time?

Yes. By connecting BillionVerify through n8n or Integrately, you can trigger a verification check the moment a chatbot captures an email, with results returned fast enough to influence the conversation flow before it ends.

What happens to role-based addresses like sales@ or info@?

BillionVerify flags role-based addresses, which are rarely tied to an individual decision-maker. You can choose to block them automatically or send them to a separate review queue rather than adding them to active sales sequences.

Does verifying emails help Drift's email deliverability?

Absolutely. Fewer bounces from invalid addresses keeps your sending domain's reputation strong, which means more of your follow-up emails from Drift land in primary inboxes instead of spam folders.

Verify emails in Drift

Create a free account, grab your API key, and stop bounces before they happen.

Get started free