{
  "name": "B2B Account Enrichment — Apollo + Gemini + Airtable",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "id": "1a2b3c4d-0001-0001-0001-000000000001",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [240, 300],
      "notes": "Polls every 15 minutes. Keep the workflow inactive until a manual dry-run passes."
    },
    {
      "parameters": {
        "operation": "list",
        "base": {
          "__rl": true,
          "value": "YOUR_AIRTABLE_BASE_ID",
          "mode": "id"
        },
        "table": {
          "__rl": true,
          "value": "YOUR_AIRTABLE_TABLE_ID",
          "mode": "id"
        },
        "filterByFormula": "={Status}=\"New\"",
        "returnAll": false,
        "limit": 10,
        "options": {}
      },
      "id": "1a2b3c4d-0002-0002-0002-000000000002",
      "name": "Airtable — List New Accounts",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2,
      "position": [460, 300],
      "credentials": {
        "airtableTokenApi": {
          "id": "YOUR_AIRTABLE_CREDENTIAL_ID",
          "name": "Airtable — account enrichment"
        }
      },
      "notes": "Fetches up to 10 records with Status = New. Preview the returned fields before enabling writes."
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-001",
              "leftValue": "={{ $json.fields['Company Domain'] }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "1a2b3c4d-0003-0003-0003-000000000003",
      "name": "Filter — Domain Not Empty",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2,
      "position": [680, 300],
      "notes": "Skips records without a company domain."
    },
    {
      "parameters": {
        "method": "GET",
        "url": "https://api.apollo.io/api/v1/organizations/enrich",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "domain",
              "value": "={{ $json.fields['Company Domain'] }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "1a2b3c4d-0004-0004-0004-000000000004",
      "name": "Apollo — Enrich Organization",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [900, 300],
      "credentials": {
        "httpHeaderAuth": {
          "id": "YOUR_APOLLO_CREDENTIAL_ID",
          "name": "Apollo API Key — x-api-key"
        }
      },
      "notes": "Returns organization data. Contact enrichment requires a separate Apollo people endpoint and is intentionally outside this template."
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:generateContent",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"contents\": [{\n    \"parts\": [{\n      \"text\": \"You are a B2B account qualification analyst. Score the organization against the stated ICP.\\n\\nICP CRITERIA:\\n- Industry: Financial Services, SaaS, PropTech, or B2B Tech\\n- Employees: 50 to 5000\\n- Geography: US, UK, Australia, or Southeast Asia\\n\\nORGANIZATION:\\nName: {{ $('Apollo — Enrich Organization').item.json.organization.name }}\\nIndustry: {{ $('Apollo — Enrich Organization').item.json.organization.industry }}\\nEmployees: {{ $('Apollo — Enrich Organization').item.json.organization.estimated_num_employees }}\\nRevenue: {{ $('Apollo — Enrich Organization').item.json.organization.annual_revenue }}\\nDescription: {{ $('Apollo — Enrich Organization').item.json.organization.short_description }}\\n\\nReturn only JSON with score A, B, C, or REVIEW and a reason of at most 20 words.\"\n    }]\n  }],\n  \"generationConfig\": {\n    \"temperature\": 0.1,\n    \"maxOutputTokens\": 150,\n    \"responseMimeType\": \"application/json\"\n  }\n}",
        "options": {}
      },
      "id": "1a2b3c4d-0005-0005-0005-000000000005",
      "name": "Gemini — Score ICP Fit",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [1120, 300],
      "credentials": {
        "httpHeaderAuth": {
          "id": "YOUR_GEMINI_CREDENTIAL_ID",
          "name": "Gemini API Key — x-goog-api-key"
        }
      },
      "notes": "Create an HTTP Header Auth credential named x-goog-api-key. Check the current Gemini model name before importing into production."
    },
    {
      "parameters": {
        "jsCode": "// Parse Gemini response safely\nconst geminiRaw = $input.first().json;\nconst textContent = geminiRaw?.candidates?.[0]?.content?.parts?.[0]?.text ?? '';\nconst cleaned = textContent.replace(/```json|```/g, '').trim();\n\nlet scoring;\ntry {\n  scoring = JSON.parse(cleaned);\n} catch (error) {\n  scoring = { score: 'REVIEW', reason: 'AI output could not be parsed.' };\n}\n\n// Preserve the original Airtable record ID across both API nodes.\nconst sourceRecord = $('Airtable — List New Accounts').item.json;\nconst recordId = sourceRecord.id;\nif (!recordId) throw new Error('Airtable record ID is missing');\n\nconst apollo = $('Apollo — Enrich Organization').item.json;\nconst org = apollo.organization || {};\n\nreturn {\n  recordId,\n  companyName: org.name || '',\n  industry: org.industry || '',\n  headcount: org.estimated_num_employees ? String(org.estimated_num_employees) : '',\n  linkedinUrl: org.linkedin_url || '',\n  description: org.short_description || '',\n  icpScore: scoring.score || 'REVIEW',\n  scoreReason: scoring.reason || '',\n  status: 'Enriched'\n};\n"
      },
      "id": "1a2b3c4d-0006-0006-0006-000000000006",
      "name": "Code — Format Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1340, 300],
      "notes": "Preserves the Airtable record ID and routes malformed AI output to REVIEW instead of silently assigning a low score."
    },
    {
      "parameters": {
        "operation": "update",
        "base": {
          "__rl": true,
          "value": "YOUR_AIRTABLE_BASE_ID",
          "mode": "id"
        },
        "table": {
          "__rl": true,
          "value": "YOUR_AIRTABLE_TABLE_ID",
          "mode": "id"
        },
        "id": "={{ $json.recordId }}",
        "fields": {
          "fieldMappingValues": [
            {
              "fieldName": "Company Name",
              "value": "={{ $json.companyName }}"
            },
            {
              "fieldName": "Industry",
              "value": "={{ $json.industry }}"
            },
            {
              "fieldName": "Headcount",
              "value": "={{ $json.headcount }}"
            },
            {
              "fieldName": "ICP Score",
              "value": "={{ $json.icpScore }}"
            },
            {
              "fieldName": "Score Reason",
              "value": "={{ $json.scoreReason }}"
            },
            {
              "fieldName": "Status",
              "value": "Enriched"
            }
          ]
        },
        "options": {}
      },
      "id": "1a2b3c4d-0007-0007-0007-000000000007",
      "name": "Airtable — Update Record",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2,
      "position": [1560, 300],
      "credentials": {
        "airtableTokenApi": {
          "id": "YOUR_AIRTABLE_CREDENTIAL_ID",
          "name": "Airtable — account enrichment"
        }
      },
      "notes": "Writes organization and score fields back by record ID. Validate field names in preview before enabling."
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [[{"node": "Airtable — List New Accounts", "type": "main", "index": 0}]]
    },
    "Airtable — List New Accounts": {
      "main": [[{"node": "Filter — Domain Not Empty", "type": "main", "index": 0}]]
    },
    "Filter — Domain Not Empty": {
      "main": [[{"node": "Apollo — Enrich Organization", "type": "main", "index": 0}]]
    },
    "Apollo — Enrich Organization": {
      "main": [[{"node": "Gemini — Score ICP Fit", "type": "main", "index": 0}]]
    },
    "Gemini — Score ICP Fit": {
      "main": [[{"node": "Code — Format Output", "type": "main", "index": 0}]]
    },
    "Code — Format Output": {
      "main": [[{"node": "Airtable — Update Record", "type": "main", "index": 0}]]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": ""
  },
  "staticData": null,
  "meta": {
    "templateCredsSetupCompleted": false,
    "description": "Configuration-reviewed account enrichment template. Replace all placeholder IDs, configure x-api-key credentials, preview mappings, and complete a manual dry-run before activation. No credentials or personal data are included."
  },
  "pinData": {},
  "versionId": "2.0.0",
  "tags": ["enrichment", "b2b", "apollo", "gemini", "airtable", "lead-scoring"]
}
