← All Newsletter & ESP Workflows
BillionVerifyPaypal

Consolidate Stripe, PayPal, Shopify and bank revenue and prepare tax filings with OpenAI

Pull contacts, verify each address with BillionVerify, and continue to PayPal — only deliverable addresses get through.

Why verify before the send

Sending to invalid, risky, catch-all, or disposable addresses spikes your bounce rate and erodes sender reputation. A verification gate before the PayPal step removes that risk automatically — only deliverable addresses continue, the rest are flagged.

The workflow

BillionVerify — verification sits right before the send.

+20
n8n steps
+2
n8n steps

Node by node

  1. 1
    OpenAI ModelSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  2. 2
    Structured Output ParserSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  3. 3
    Monthly revenue aggregationTrigger· n8n

    Starts the workflow — on a schedule, a webhook, or manually while you test.

  4. 4
    Workflow ConfigurationSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  5. 5
    Get Stripe TransactionsSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  6. 6
    Get PayPal TransactionsSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  7. 7
    Get Shopify OrdersSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  8. 8
    Get Bank Feed DataSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  9. 9
    Normalize Stripe DataSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  10. 10
    Normalize PayPal DataSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  11. 11
    Normalize Shopify DataSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  12. 12
    Normalize Bank DataSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  13. 13
    Merge All Revenue SourcesSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  14. 14
    AI Income CategorizerSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  15. 15
    Calculate Period TotalsSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  16. 16
    Format as CSVSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  17. 17
    Format as XMLSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  18. 18
    Check Submission MethodLogic· n8n

    Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.

  19. 19
    Archive to Google DriveSource· n8n

    Provides or transforms the contact data flowing through the workflow.

  20. 20
    Submit to Government APISource· n8n

    Provides or transforms the contact data flowing through the workflow.

  21. 21
    Verify Email (BillionVerify)Verify· billionverify

    The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.

  22. 22
    IF deliverableLogic· n8n

    Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.

  23. 23
    Email to Tax AgentSend· n8n

    Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.

Workflow JSON

Copy or download this workflow, then import it in n8n (Workflows → Import from File / Paste). Install the BillionVerify community node first, then add your API key credential.

verify-emails-in-paypal.json
{
  "name": "Consolidate Stripe, PayPal, Shopify and bank revenue and prepare tax filings with OpenAI + BillionVerify",
  "nodes": [
    {
      "id": "827cbd50-730e-4ddf-93dc-fa68d0724ff9",
      "name": "Workflow Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -224,
        288
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "taxPeriodStart",
              "type": "string",
              "value": "={{ $now.minus({ months: 1 }).startOf('month').toISO() }}"
            },
            {
              "id": "id-2",
              "name": "taxPeriodEnd",
              "type": "string",
              "value": "={{ $now.minus({ months: 1 }).endOf('month').toISO() }}"
            },
            {
              "id": "id-3",
              "name": "submissionMethod",
              "type": "string",
              "value": "api"
            },
            {
              "id": "id-4",
              "name": "taxAgentEmail",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Tax Agent Email Address__>"
            },
            {
              "id": "id-5",
              "name": "governmentApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Government Tax API Endpoint__>"
            },
            {
              "id": "id-6",
              "name": "bankFeedApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Bank Feed API Endpoint__>"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "74d8059b-52fe-4b35-bc6a-c89a90803868",
      "name": "Get Stripe Transactions",
      "type": "n8n-nodes-base.stripe",
      "position": [
        0,
        80
      ],
      "parameters": {
        "limit": 100,
        "resource": "charge",
        "operation": "getAll"
      },
      "typeVersion": 1
    },
    {
      "id": "48dce256-d478-4814-a1c3-48b0087f57e9",
      "name": "Get PayPal Transactions",
      "type": "n8n-nodes-base.payPal",
      "position": [
        0,
        272
      ],
      "parameters": {
        "resource": "payoutItem",
        "payoutItemId": "<__PLACEHOLDER_VALUE__Payout Item ID__>"
      },
      "typeVersion": 1
    },
    {
      "id": "475587a0-60f5-45c5-b7dc-e8c5c28d613d",
      "name": "Get Shopify Orders",
      "type": "n8n-nodes-base.shopify",
      "position": [
        0,
        464
      ],
      "parameters": {
        "options": {
          "createdAtMax": "={{ $('Workflow Configuration').first().json.taxPeriodEnd }}",
          "createdAtMin": "={{ $('Workflow Configuration').first().json.taxPeriodStart }}"
        },
        "operation": "getAll",
        "returnAll": true
      },
      "typeVersion": 1
    },
    {
      "id": "ba27bdb1-ee18-4fd0-878b-8d19a0fc9db6",
      "name": "Get Bank Feed Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        656
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.bankFeedApiUrl }}",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "start_date",
              "value": "={{ $('Workflow Configuration').first().json.taxPeriodStart }}"
            },
            {
              "name": "end_date",
              "value": "={{ $('Workflow Configuration').first().json.taxPeriodEnd }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "627e15cc-7a89-4d09-9f13-bfc21360c1ca",
      "name": "Normalize Stripe Data",
      "type": "n8n-nodes-base.set",
      "position": [
        208,
        112
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "source",
              "type": "string",
              "value": "Stripe"
            },
            {
              "id": "id-2",
              "name": "transactionId",
              "type": "string",
              "value": "={{ $json.id }}"
            },
            {
              "id": "id-3",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.amount / 100 }}"
            },
            {
              "id": "id-4",
              "name": "currency",
              "type": "string",
              "value": "={{ $json.currency }}"
            },
            {
              "id": "id-5",
              "name": "date",
              "type": "string",
              "value": "={{ new Date($json.created * 1000).toISOString() }}"
            },
            {
              "id": "id-6",
              "name": "description",
              "type": "string",
              "value": "={{ $json.description }}"
            },
            {
              "id": "id-7",
              "name": "customerEmail",
              "type": "string",
              "value": "={{ $json.billing_details?.email }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "69974097-d415-4b94-93bb-2ffa7796026e",
      "name": "Normalize PayPal Data",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        272
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "source",
              "type": "string",
              "value": "PayPal"
            },
            {
              "id": "id-2",
              "name": "transactionId",
              "type": "string",
              "value": "={{ $json.payout_item_id }}"
            },
            {
              "id": "id-3",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.payout_item.amount.value }}"
            },
            {
              "id": "id-4",
              "name": "currency",
              "type": "string",
              "value": "={{ $json.payout_item.amount.currency }}"
            },
            {
              "id": "id-5",
              "name": "date",
              "type": "string",
              "value": "={{ $json.time_processed }}"
            },
            {
              "id": "id-6",
              "name": "description",
              "type": "string",
              "value": "={{ $json.payout_item.transaction_note }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "7a796a3c-a049-48e7-b7b9-ce5cab1a9dd3",
      "name": "Normalize Shopify Data",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        464
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "source",
              "type": "string",
              "value": "Shopify"
            },
            {
              "id": "id-2",
              "name": "transactionId",
              "type": "string",
              "value": "={{ $json.id }}"
            },
            {
              "id": "id-3",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.total_price }}"
            },
            {
              "id": "id-4",
              "name": "currency",
              "type": "string",
              "value": "={{ $json.currency }}"
            },
            {
              "id": "id-5",
              "name": "date",
              "type": "string",
              "value": "={{ $json.created_at }}"
            },
            {
              "id": "id-6",
              "name": "description",
              "type": "string",
              "value": "={{ $json.name }}"
            },
            {
              "id": "id-7",
              "name": "customerEmail",
              "type": "string",
              "value": "={{ $json.email }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "9e2c41c2-5690-4081-8594-8832a3f4eef1",
      "name": "Normalize Bank Data",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        656
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "source",
              "type": "string",
              "value": "Bank"
            },
            {
              "id": "id-2",
              "name": "transactionId",
              "type": "string",
              "value": "={{ $json.transaction_id }}"
            },
            {
              "id": "id-3",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.amount }}"
            },
            {
              "id": "id-4",
              "name": "currency",
              "type": "string",
              "value": "={{ $json.currency }}"
            },
            {
              "id": "id-5",
              "name": "date",
              "type": "string",
              "value": "={{ $json.date }}"
            },
            {
              "id": "id-6",
              "name": "description",
              "type": "string",
              "value": "={{ $json.description }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "2a4f11e4-4d51-4c71-baf6-ff241c193e3d",
      "name": "Merge All Revenue Sources",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        448,
        288
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "allTransactions"
      },
      "typeVersion": 1
    },
    {
      "id": "833c661f-0aff-4be4-834a-108c40f92725",
      "name": "AI Income Categorizer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        672,
        288
      ],
      "parameters": {
        "text": "={{ 'Categorize these revenue transactions: ' + JSON.stringify($json.allTransactions) }}",
        "options": {
          "systemMessage": "You are a tax categorization assistant. Your task is to analyze revenue transactions and categorize each one according to standard income categories.\n\nFor each transaction, determine:\n1. incomeCategory: The type of income (e.g., \"Product Sales\", \"Service Revenue\", \"Subscription Revenue\", \"Interest Income\", \"Other Income\")\n2. taxable: Whether the income is taxable (true/false)\n3. taxRate: The applicable tax rate as a decimal (e.g., 0.10 for 10%)\n4. notes: Any relevant notes about the categorization\n\nReturn the categorized transactions in the exact structure defined by the output parser."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3
    },
    {
      "id": "9497a3e4-6beb-4aff-9032-a765685fc98a",
      "name": "OpenAI Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        688,
        512
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "mv2ECvRtbAK63G2g",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "07444727-4739-44ed-8312-b47e24da62d9",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        816,
        512
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"transactions\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"transactionId\": {\n            \"type\": \"string\"\n          },\n          \"source\": {\n            \"type\": \"string\"\n          },\n          \"amount\": {\n            \"type\": \"number\"\n          },\n          \"currency\": {\n            \"type\": \"string\"\n          },\n          \"date\": {\n            \"type\": \"string\"\n          },\n          \"description\": {\n            \"type\": \"string\"\n          },\n          \"incomeCategory\": {\n            \"type\": \"string\"\n          },\n          \"taxable\": {\n            \"type\": \"boolean\"\n          },\n          \"taxRate\": {\n            \"type\": \"number\"\n          },\n          \"notes\": {\n            \"type\": \"string\"\n          }\n        }\n      }\n    }\n  }\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "d531269c-0591-4ba6-af34-e68613c12329",
      "name": "Calculate Period Totals",
      "type": "n8n-nodes-base.code",
      "position": [
        1024,
        288
      ],
      "parameters": {
        "jsCode": "// Calculate Period Totals for Tax Filing\n// This code processes categorized income data and calculates totals by category\n\nconst items = $input.all();\n\n// Initialize totals object\nconst totals = {\n  byCategory: {},\n  totalRevenue: 0,\n  totalTaxableAmount: 0,\n  totalTax: 0,\n  transactionCount: 0,\n  periodStart: null,\n  periodEnd: null\n};\n\n// Process each transaction\nfor (const item of items) {\n  const data = item.json;\n  \n  // Extract relevant fields\n  const category = data.category || 'Uncategorized';\n  const amount = parseFloat(data.amount) || 0;\n  const taxableAmount = parseFloat(data.taxableAmount) || amount;\n  const taxRate = parseFloat(data.taxRate) || 0;\n  const tax = taxableAmount * (taxRate / 100);\n  const date = data.date || data.transactionDate;\n  \n  // Update category totals\n  if (!totals.byCategory[category]) {\n    totals.byCategory[category] = {\n      revenue: 0,\n      taxableAmount: 0,\n      tax: 0,\n      count: 0\n    };\n  }\n  \n  totals.byCategory[category].revenue += amount;\n  totals.byCategory[category].taxableAmount += taxableAmount;\n  totals.byCategory[category].tax += tax;\n  totals.byCategory[category].count += 1;\n  \n  // Update overall totals\n  totals.totalRevenue += amount;\n  totals.totalTaxableAmount += taxableAmount;\n  totals.totalTax += tax;\n  totals.transactionCount += 1;\n  \n  // Track period dates\n  if (date) {\n    const transactionDate = new Date(date);\n    if (!totals.periodStart || transactionDate < totals.periodStart) {\n      totals.periodStart = transactionDate;\n    }\n    if (!totals.periodEnd || transactionDate > totals.periodEnd) {\n      totals.periodEnd = transactionDate;\n    }\n  }\n}\n\n// Format dates\nif (totals.periodStart) {\n  totals.periodStart = totals.periodStart.toISOString().split('T')[0];\n}\nif (totals.periodEnd) {\n  totals.periodEnd = totals.periodEnd.toISOString().split('T')[0];\n}\n\n// Round all monetary values to 2 decimal places\ntotals.totalRevenue = Math.round(totals.totalRevenue * 100) / 100;\ntotals.totalTaxableAmount = Math.round(totals.totalTaxableAmount * 100) / 100;\ntotals.totalTax = Math.round(totals.totalTax * 100) / 100;\n\nfor (const category in totals.byCategory) {\n  totals.byCategory[category].revenue = Math.round(totals.byCategory[category].revenue * 100) / 100;\n  totals.byCategory[category].taxableAmount = Math.round(totals.byCategory[category].taxableAmount * 100) / 100;\n  totals.byCategory[category].tax = Math.round(totals.byCategory[category].tax * 100) / 100;\n}\n\n// Create summary output\nconst summary = {\n  summary: totals,\n  categories: Object.keys(totals.byCategory).map(category => ({\n    category: category,\n    ...totals.byCategory[category]\n  })),\n  transactions: items.map(item => item.json)\n};\n\nreturn [summary];"
      },
      "typeVersion": 2
    },
    {
      "id": "d739f8aa-6436-4d5d-9b54-b33c39eb5b4e",
      "name": "Format as CSV",
      "type": "n8n-nodes-base.code",
      "position": [
        1248,
        192
      ],
      "parameters": {
        "jsCode": "// Convert categorized transaction data and totals into CSV format\nconst items = $input.all();\n\n// Define CSV headers\nconst headers = [\n  'Transaction ID',\n  'Source',\n  'Date',\n  'Amount',\n  'Currency',\n  'Category',\n  'Taxable',\n  'Tax Rate',\n  'Tax Amount',\n  'Description'\n];\n\n// Create CSV rows\nconst rows = items.map(item => {\n  const data = item.json;\n  return [\n    data.transactionId || data.transaction_id || '',\n    data.source || '',\n    data.date || '',\n    data.amount || '',\n    data.currency || '',\n    data.category || '',\n    data.taxable || '',\n    data.taxRate || data.tax_rate || '',\n    data.taxAmount || data.tax_amount || '',\n    data.description || ''\n  ];\n});\n\n// Escape CSV fields (handle commas, quotes, newlines)\nconst escapeCSVField = (field) => {\n  const fieldStr = String(field);\n  if (fieldStr.includes(',') || fieldStr.includes('\"') || fieldStr.includes('\\n')) {\n    return '\"' + fieldStr.replace(/\"/g, '\"\"') + '\"';\n  }\n  return fieldStr;\n};\n\n// Build CSV content\nconst csvRows = [\n  headers.map(escapeCSVField).join(','),\n  ...rows.map(row => row.map(escapeCSVField).join(','))\n];\n\nconst csvContent = csvRows.join('\\n');\n\n// Return CSV as output\nreturn [\n  {\n    json: {\n      csv: csvContent,\n      format: 'csv',\n      rowCount: rows.length,\n      generatedAt: new Date().toISOString()\n    },\n    binary: {\n      data: {\n        data: Buffer.from(csvContent).toString('base64'),\n        mimeType: 'text/csv',\n        fileName: `revenue_report_${new Date().toISOString().split('T')[0]}.csv`\n      }\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "d22f7d8e-0094-4dfb-b29e-c2d28da87040",
      "name": "Format as XML",
      "type": "n8n-nodes-base.code",
      "position": [
        1696,
        480
      ],
      "parameters": {
        "jsCode": "// Convert categorized transaction data and totals into XML format for government tax filing\nconst items = $input.all();\n\n// Helper function to escape XML special characters\nfunction escapeXml(unsafe) {\n  if (unsafe === null || unsafe === undefined) return '';\n  return String(unsafe)\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;')\n    .replace(/'/g, '&apos;');\n}\n\n// Helper function to format date to ISO format\nfunction formatDate(date) {\n  if (!date) return new Date().toISOString().split('T')[0];\n  return new Date(date).toISOString().split('T')[0];\n}\n\n// Build XML structure\nlet xml = '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n';\nxml += '<TaxFilingReport>\\n';\nxml += '  <Header>\\n';\nxml += `    <SubmissionDate>${formatDate(new Date())}</SubmissionDate>\\n`;\nxml += `    <TaxPeriod>${items[0]?.json?.taxPeriod || 'QUARTERLY'}</TaxPeriod>\\n`;\nxml += `    <FiscalYear>${new Date().getFullYear()}</FiscalYear>\\n`;\nxml += `    <TaxpayerID>${items[0]?.json?.taxpayerId || 'TBD'}</TaxpayerID>\\n`;\nxml += '  </Header>\\n';\n\n// Add summary totals\nxml += '  <Summary>\\n';\nconst totals = items[0]?.json?.totals || {};\nxml += `    <TotalRevenue>${totals.totalRevenue || 0}</TotalRevenue>\\n`;\nxml += `    <TotalTaxableIncome>${totals.totalTaxableIncome || 0}</TotalTaxableIncome>\\n`;\nxml += `    <TotalDeductions>${totals.totalDeductions || 0}</TotalDeductions>\\n`;\nxml += `    <NetTaxableAmount>${totals.netTaxableAmount || 0}</NetTaxableAmount>\\n`;\nxml += `    <TransactionCount>${items.length}</TransactionCount>\\n`;\nxml += '  </Summary>\\n';\n\n// Add category breakdown\nxml += '  <CategoryBreakdown>\\n';\nconst categories = items[0]?.json?.categoryTotals || {};\nfor (const [category, amount] of Object.entries(categories)) {\n  xml += '    <Category>\\n';\n  xml += `      <Name>${escapeXml(category)}</Name>\\n`;\n  xml += `      <Amount>${amount}</Amount>\\n`;\n  xml += '    </Category>\\n';\n}\nxml += '  </CategoryBreakdown>\\n';\n\n// Add individual transactions\nxml += '  <Transactions>\\n';\nfor (const item of items) {\n  const data = item.json;\n  xml += '    <Transaction>\\n';\n  xml += `      <TransactionID>${escapeXml(data.transactionId || data.id)}</TransactionID>\\n`;\n  xml += `      <Date>${formatDate(data.date || data.transactionDate)}</Date>\\n`;\n  xml += `      <Source>${escapeXml(data.source || 'UNKNOWN')}</Source>\\n`;\n  xml += `      <Amount>${data.amount || 0}</Amount>\\n`;\n  xml += `      <Currency>${escapeXml(data.currency || 'USD')}</Currency>\\n`;\n  xml += `      <Category>${escapeXml(data.category || 'UNCATEGORIZED')}</Category>\\n`;\n  xml += `      <Description>${escapeXml(data.description || '')}</Description>\\n`;\n  xml += `      <TaxStatus>${escapeXml(data.taxStatus || 'TAXABLE')}</TaxStatus>\\n`;\n  if (data.customerName) {\n    xml += `      <CustomerName>${escapeXml(data.customerName)}</CustomerName>\\n`;\n  }\n  xml += '    </Transaction>\\n';\n}\nxml += '  </Transactions>\\n';\n\nxml += '</TaxFilingReport>';\n\n// Return the XML as output\nreturn [{\n  json: {\n    xml: xml,\n    format: 'XML',\n    generatedAt: new Date().toISOString(),\n    recordCount: items.length\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "62bba3e6-8d63-4684-a961-d6b783aea349",
      "name": "Check Submission Method",
      "type": "n8n-nodes-base.if",
      "position": [
        1472,
        192
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('Workflow Configuration').first().json.submissionMethod }}",
              "rightValue": "api"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "35e46f44-2e36-4fb6-927d-c4981615836b",
      "name": "Submit to Government API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1696,
        96
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.governmentApiUrl }}",
        "body": "={{ $json.csvData }}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "raw",
        "sendHeaders": true,
        "rawContentType": "text/csv",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "text/csv"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "46772d2d-fb8f-47bb-8943-ef32f2bed8ab",
      "name": "Email to Tax Agent",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1696,
        288
      ],
      "webhookId": "103d107e-b3c2-4f50-b9a8-84fbb984c6e0",
      "parameters": {
        "sendTo": "={{ $('Workflow Configuration').first().json.taxAgentEmail }}",
        "message": "={{ 'Dear Tax Agent,<br><br>Please find attached the tax filing submission for the period ' + $('Workflow Configuration').first().json.taxPeriodStart + ' to ' + $('Workflow Configuration').first().json.taxPeriodEnd + '.<br><br>Revenue Summary:<br>' + JSON.stringify($('Calculate Period Totals').first().json, null, 2).replace(/\\n/g, '<br>') + '<br><br>The detailed CSV file is attached.<br><br>Best regards,<br>Automated Tax Filing System' }}",
        "options": {
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "csvData"
              }
            ]
          }
        },
        "subject": "={{ 'Tax Filing Submission - Period: ' + $('Workflow Configuration').first().json.taxPeriodStart + ' to ' + $('Workflow Configuration').first().json.taxPeriodEnd }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "u1N5nBDvQ0AWhNnV",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "82060b39-886f-4e46-96ad-5f837116f1eb",
      "name": "Archive to Google Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1920,
        288
      ],
      "parameters": {
        "name": "={{ 'tax_filing_' + new Date().toISOString().split('T')[0] + '.csv' }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {
          "simplifyOutput": true
        },
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "root",
          "cachedResultName": "/ (Root folder)"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "ALHOS4YihnICuh2c",
          "name": "Google Drive account"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "43b5a026-1918-4f2b-a76c-3271ba1fd3d4",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1136,
        -288
      ],
      "parameters": {
        "color": 5,
        "width": 400,
        "height": 208,
        "content": "## Customization\nModify normalization rules per jurisdiction; add expense categories to AI prompt;  \n\n## Benefits\nEliminates manual reconciliation; reduces tax filing time by 80%; improves accuracy; "
      },
      "typeVersion": 1
    },
    {
      "id": "25215ef2-8cb0-41e3-8f17-706714474149",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        672,
        -288
      ],
      "parameters": {
        "color": 4,
        "width": 416,
        "height": 208,
        "content": "## Prerequisites\nStripe, PayPal, Shopify, or bank APIs; OpenAI account; Google Workspace;  \n\n## Use Cases\nQuarterly tax preparation for e-commerce; multi-channel revenue reconciliation; "
      },
      "typeVersion": 1
    },
    {
      "id": "219f5590-aeb3-44ac-b607-0ff38da38c07",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        272,
        -288
      ],
      "parameters": {
        "width": 320,
        "height": 208,
        "content": "## Setup Steps\n1. Connect Stripe/PayPal/Shopify accounts with API keys to respective nodes.\n2. Configure bank feed authentication \n3. Set OpenAI credentials for AI Income Categorizer node.\n4. Link Google Drive and Gmail .\n"
      },
      "typeVersion": 1
    },
    {
      "id": "fc759cae-6766-43e5-b517-e03f9bffb698",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        -288
      ],
      "parameters": {
        "width": 752,
        "height": 192,
        "content": "## How It Works\nConsolidates daily revenue from Stripe, PayPal, Shopify, and bank feeds into a single system. The workflow automatically normalizes data across payment sources, uses AI to categorize income transactions, calculates reporting-period totals, and generates tax-compliant CSV and XML submissions. Designed for e-commerce businesses, SaaS companies, and multi-channel retailers managing complex revenue streams, it eliminates manual reconciliation, reduces filing errors, and speeds up financial reporting by automating the entire pipeline from data collection to government submission."
      },
      "typeVersion": 1
    },
    {
      "id": "89107252-fe33-4736-aa8f-f364ec4a1f86",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        656,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 304,
        "height": 880,
        "content": "## AI-Powered Income Categorization\n**What:** Use all normalized streams and categorizes income using AI-powered transaction analysis.\n**Why:** Automates income classification and detects anomalies "
      },
      "typeVersion": 1
    },
    {
      "id": "bc7e0768-29f3-4c48-930d-f68f583ba464",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        976,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 1104,
        "height": 864,
        "content": "## Tax Period Calculation and submission\n**What:** Computes period totals and tax period summaries with validation checks.\n**Why:** Ensures accuracy and compliance-ready figures for regulatory submission."
      },
      "typeVersion": 1
    },
    {
      "id": "666e7b09-aefd-4026-a92b-3fd11ce34648",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        144,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 496,
        "height": 864,
        "content": "\n## Data Normalization\n**What:** Applies format-standardization and transformation rules to each data source independently.\n**Why:** Creates consistent data structure  "
      },
      "typeVersion": 1
    },
    {
      "id": "7ea72134-db7a-490c-9bad-91f93cdebb1e",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 848,
        "content": "## Multi-Source Data Collection\n**What:** Retrieves raw transaction data from four distinct payment sources  \n**Why:** Ensures complete revenue visibility across all channels."
      },
      "typeVersion": 1
    },
    {
      "id": "a6403c5a-d2fa-45ba-9e73-96d5ee5374fe",
      "name": "Monthly revenue aggregation",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -448,
        288
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 2
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "parameters": {
        "operation": "verify",
        "email": "={{ $('Workflow Configuration').first().json.taxAgentEmail }}",
        "additionalOptions": {}
      },
      "type": "n8n-nodes-billionverify.billionVerify",
      "typeVersion": 1,
      "position": [
        1336,
        288
      ],
      "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": [
        1516,
        288
      ],
      "name": "IF deliverable"
    }
  ],
  "connections": {
    "OpenAI Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Income Categorizer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Format as CSV": {
      "main": [
        [
          {
            "node": "Check Submission Method",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format as XML": {
      "main": [
        [
          {
            "node": "Archive to Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email to Tax Agent": {
      "main": [
        [
          {
            "node": "Archive to Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Bank Feed Data": {
      "main": [
        [
          {
            "node": "Normalize Bank Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Shopify Orders": {
      "main": [
        [
          {
            "node": "Normalize Shopify Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Bank Data": {
      "main": [
        [
          {
            "node": "Merge All Revenue Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Income Categorizer": {
      "main": [
        [
          {
            "node": "Calculate Period Totals",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize PayPal Data": {
      "main": [
        [
          {
            "node": "Merge All Revenue Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Stripe Data": {
      "main": [
        [
          {
            "node": "Merge All Revenue Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Shopify Data": {
      "main": [
        [
          {
            "node": "Merge All Revenue Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration": {
      "main": [
        [
          {
            "node": "Get Stripe Transactions",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get PayPal Transactions",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Shopify Orders",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Bank Feed Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Period Totals": {
      "main": [
        [
          {
            "node": "Format as CSV",
            "type": "main",
            "index": 0
          },
          {
            "node": "Format as XML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Submission Method": {
      "main": [
        [
          {
            "node": "Submit to Government API",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Verify Email (BillionVerify)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get PayPal Transactions": {
      "main": [
        [
          {
            "node": "Normalize PayPal Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Stripe Transactions": {
      "main": [
        [
          {
            "node": "Normalize Stripe Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Income Categorizer",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Submit to Government API": {
      "main": [
        [
          {
            "node": "Archive to Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Revenue Sources": {
      "main": [
        [
          {
            "node": "AI Income Categorizer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Monthly revenue aggregation": {
      "main": [
        [
          {
            "node": "Workflow Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify Email (BillionVerify)": {
      "main": [
        [
          {
            "node": "IF deliverable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF deliverable": {
      "main": [
        [
          {
            "node": "Email to Tax Agent",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}

When to use this

  • Cleaning a list before a PayPal send or sync.
  • Protecting PayPal deliverability and sender reputation.
  • Keeping bounce rates low so your sending is never throttled.

FAQ

Why verify before sending in PayPal?

Verifying first keeps your bounce rate low, which protects your sender reputation and your results.

How do I import this workflow?

Download the JSON, then in n8n go to Workflows → Import from File (or paste it). Install the BillionVerify community node and add your API key credential.

What happens to risky or catch-all addresses?

They are routed to the false branch and excluded from the send. You decide whether to retry, review, or drop them.

Add verification to your workflow

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

Get started free