This workflow corresponds to n8n.io template #16782 — we link there as the canonical source.
This workflow follows the Agent → Gmail recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "I4DZYq0e7ZgxSReh",
"meta": {
"builderVariant": "mcp",
"aiBuilderAssisted": true,
"templateCredsSetupCompleted": true
},
"name": "Qualify and route free signups with Twain",
"tags": [
{
"id": "RbQkDoqE7tO0Uf0S",
"name": "Qualify lead",
"createdAt": "2026-06-26T10:33:33.657Z",
"updatedAt": "2026-06-26T10:33:33.657Z"
},
{
"id": "fCnrltXSe85Ec1I8",
"name": "n8n",
"createdAt": "2026-06-26T10:33:26.707Z",
"updatedAt": "2026-06-26T10:33:26.707Z"
},
{
"id": "wZk8565KQmd7c0S5",
"name": "Twain",
"createdAt": "2026-06-26T10:33:19.597Z",
"updatedAt": "2026-06-26T10:33:19.597Z"
}
],
"nodes": [
{
"id": "e5b87045-866c-4179-8f5f-039aa20374be",
"name": "Signup Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
240,
528
],
"parameters": {
"path": "signup-lead-qualify",
"options": {},
"httpMethod": "POST",
"authentication": "headerAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "e136bab6-5b4b-44b8-8661-d4eb4847cfad",
"name": "Has usable contact?",
"type": "n8n-nodes-base.if",
"position": [
3280,
560
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "usable-1",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ Boolean($json.has_work_email) || Boolean($json.has_linkedin_url) }}",
"rightValue": ""
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.3
},
{
"id": "3071075f-8355-4380-82c0-baa8e2f57d58",
"name": "Slack: Personal email",
"type": "n8n-nodes-base.slack",
"onError": "continueRegularOutput",
"position": [
3664,
752
],
"parameters": {
"text": "=:no_entry_sign: *Twain \u2014 Qualify & Route Free Signup \u2014 skipped*\n*{{ $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email || 'Unknown signup' }}* ({{ $('Normalize Signup').item.json.email || 'No email provided' }})\n{{ (($('Normalize Signup').item.json.linkedin_url) || '') ? '<' + (($('Normalize Signup').item.json.linkedin_url)) + '|LinkedIn profile>' : '' }}\nNo work email, and the LinkedIn lookup didn't return a profile URL for this signup \u2014 so there's nothing to research or qualify. Enable a LinkedIn Lookup provider (or add a work email / LinkedIn URL upstream) to catch leads like this.\n<https://www.twain.ai/w|Open Twain workspace>",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.slack_channel }}"
},
"otherOptions": {
"unfurl_links": false,
"unfurl_media": false,
"includeLinkToWorkflow": false
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
},
{
"id": "57f5a107-40c8-41c7-8df6-18dc5bab8278",
"name": "Twain Generate Research",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
3664,
560
],
"parameters": {
"url": "https://public.api.twain.ai/v2/Generate/Research",
"method": "POST",
"options": {
"timeout": 180000
},
"jsonBody": "={{ {\n campaign_id: $('Normalize Signup').item.json.campaign_id,\n contact: {\n ...($('Normalize Signup').item.json.has_work_email ? { work_email: $('Normalize Signup').item.json.email } : {}),\n ...($json.has_linkedin_url ? { linkedin_profile_url: $json.linkedin_url } : {}),\n },\n add_contact_to_campaign: true,\n} }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "ac4d8c53-4292-47ba-9cea-e5c59befd797",
"name": "Stage A \u2014 Hard Disqualify?",
"type": "n8n-nodes-base.if",
"position": [
4048,
560
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "or",
"conditions": [
{
"id": "8e8de89c-59d1-4dff-8271-81a3896c5f54",
"operator": {
"type": "array",
"operation": "contains",
"rightType": "any"
},
"leftValue": "={{ $('Twain Generate Research').item.json.warnings.categories }}",
"rightValue": "PERSONA_MISMATCH"
},
{
"id": "99e07edb-fc06-4ba3-86e2-4995a996d349",
"operator": {
"type": "array",
"operation": "contains",
"rightType": "any"
},
"leftValue": "={{ $('Twain Generate Research').item.json.warnings.categories }}",
"rightValue": "INCOMPLETE_PROFILE"
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.3
},
{
"id": "5a8ec23c-c547-489d-9e62-b9f54b76de37",
"name": "Stage B \u2014 Fit Judge",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
4288,
688
],
"parameters": {
"text": "=Prospect company: {{ $('Twain Generate Research').item.json.research?.company?.name || $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email_domain }}\nWebsite: {{ $('Twain Generate Research').item.json.research?.company?.website || '' }}\nCompany size: {{ $('Twain Generate Research').item.json.research?.company?.size || 'unknown' }}\nCompany description: {{ $('Twain Generate Research').item.json.research?.company?.description || '' }}\n\nIndividual: {{ $('Normalize Signup').item.json.displayName || '' }} \u2014 {{ $('Twain Generate Research').item.json.research?.person?.title || '' }} ({{ $('Normalize Signup').item.json.email }})\nHeadline: {{ $('Twain Generate Research').item.json.research?.person?.headline || '' }}\n\nResearch signals:\n- Warnings: {{ JSON.stringify($('Twain Generate Research').item.json.warnings) }}\n- Why now: {{ $('Twain Generate Research').item.json.research?.expanded_research?.why_now?.markdown || 'n/a' }}\n- Why us: {{ $('Twain Generate Research').item.json.research?.expanded_research?.why_us?.markdown || 'n/a' }}\n- Insights: {{ $('Twain Generate Research').item.json.research?.expanded_research?.insights?.markdown || '' }}",
"options": {
"systemMessage": "You qualify whether a PROSPECT company is an ICP fit for a B2B product. Be decisive and evidence-based.\n\n=== THE PRODUCT YOU QUALIFY FOR (edit this section for your company) ===\nProduct: Twain \u2014 https://twain.ai\nWhat it does: Twain is an AI sales-research-and-writing platform that turns real-time research into personalized, on-brand B2B outreach (cold email + LinkedIn), with native syncs to tools like Clay and HubSpot. If a web-search tool is connected, search the web (including the product URL) to confirm the current positioning, since it changes often.\n\n=== JOB-TO-BE-DONE THE PRODUCT SERVES ===\n\"When my top-of-funnel outreach hits a performance ceiling because the copy is too generic for high-value accounts, I want an automated system for highly personalized, relevant, scalable communications that natively syncs with my stack (e.g. Clay, HubSpot) so I can launch signal-based engagement and prove the ROI of Demand Gen \u2014 without manual prospecting or risking brand reputation through hallucinated AI.\"\nThe closer the prospect resembles a company that would have this job, the better the fit.\n\n=== ICP TIERS (edit for your company) ===\nTIER 1 (strongest) \u2014 ALL of: >=100 employees; HQ in Europe or North America; raised at least Series B; the individual prospect works in Demand Generation, Growth, GTM, or RevOps. Reference Tier-1 customers (look for resemblance): ashbyhq.com, xentral.com, everstage.com, graviteesource.com, rechargeapps.com, factorialhr.co, tracksuit.com.\nTIER 2 (good) \u2014 ALL of: >=11 and <5000 employees; a genuine B2B company with a B2B offering; at least 1 year old; NOT a lead-generation agency, recruiting agency, freelancer, or marketing agency. Reference Tier-2 customers: vumo.ai, nory.ai, getnnuvo.com, cotiss.com.\nNO FIT \u2014 fails the Tier-2 bar, or is a disqualified type (lead-gen/recruiting/marketing agency, freelancer, B2C-only, <1 year old, >5000 employees, or clearly off-ICP).\n\n=== HOW TO JUDGE ===\n1. Understand the product (above; use a connected web-search tool if available).\n2. Understand the prospect from the research provided in the user message (company size, description, region, funding/maturity, B2B model, the individual's role) plus the why-now / why-us / warnings signals; if a web-search tool is connected, also look up the prospect's website to confirm.\n3. Compare against the tiers and reference customers; assess the job-to-be-done fit.\n4. Pick the single best tier. If a hard Tier-1 criterion is unknown, do not assume it \u2014 fall to Tier 2 if Tier-2 criteria are met, else NO FIT.\n\nReturn ONLY the structured object: analysis (comprehensive reasoning citing the strongest concrete signals \u2014 size, region, funding, role, B2B model, resemblance to reference customers, JTBD match); summary (1-2 sentences); tier (exactly \"TIER 1\", \"TIER 2\", or \"NO FIT\")."
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 3.1
},
{
"id": "0f7239d7-3799-4f65-a99c-9c60271da518",
"name": "Claude Sonnet 4.6",
"type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
"position": [
4160,
848
],
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "claude-sonnet-4-6",
"cachedResultName": "Claude Sonnet 4.6"
},
"options": {
"temperature": 0.1
}
},
"credentials": {
"anthropicApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.5
},
{
"id": "0248db4c-fdf0-477f-8d2d-9ecfd931dd06",
"name": "Fit Verdict Schema",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
4544,
848
],
"parameters": {
"jsonSchemaExample": "{ \"analysis\": \"RevOps leader at a Series-C B2B SaaS (~400 FTE, US) running outbound \u2014 closely resembles Everstage/Ashby; strong JTBD match.\", \"summary\": \"Strong Tier-1 fit: large NA B2B SaaS with a RevOps buyer and a clear personalization-at-scale need.\", \"tier\": \"TIER 1\" }"
},
"typeVersion": 1.3
},
{
"id": "c5a14b42-2b83-41bf-98f6-e4151bd5d5b0",
"name": "Is High Fit?",
"type": "n8n-nodes-base.if",
"position": [
4800,
528
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "4300371c-0841-48d9-8392-39f6f9dd308c",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.output?.tier ?? $json.tier ?? '' }}",
"rightValue": "TIER 1"
},
{
"id": "cef49b99-1cd3-4b4e-991e-40d38552f542",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.output?.tier ?? $json.tier ?? '' }}",
"rightValue": "TIER 2"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "97d7a1ee-16d0-4b46-8c99-5a95e0459b2a",
"name": "High Fit (A/B)",
"type": "n8n-nodes-base.noOp",
"position": [
5184,
528
],
"parameters": {},
"executeOnce": false,
"typeVersion": 1
},
{
"id": "b3aaea39-c00c-4445-8e32-6f31f0d3ae22",
"name": "Slack: Twain error",
"type": "n8n-nodes-base.slack",
"onError": "continueRegularOutput",
"position": [
3904,
752
],
"parameters": {
"text": "=:warning: *Twain research failed*\n*{{ $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email || 'Unknown signup' }}* ({{ $('Normalize Signup').item.json.email || 'No email provided' }})\n{{ (($('Normalize Signup').item.json.linkedin_url) || '') ? '<' + (($('Normalize Signup').item.json.linkedin_url)) + '|LinkedIn profile>' : '' }}\n*Error:*\n{{ ((String($json.error?.message ?? $json.message ?? '').replace(/^\\s*\\d{3}\\s*-\\s*/, '').trim()).includes('\"message\":\"') ? (((String($json.error?.message ?? $json.message ?? '').replace(/^\\s*\\d{3}\\s*-\\s*/, '').trim()).split('\"message\":\"')[1] || '').split('\"')[0].replace(/\\\\/g, '')) : String($json.error?.message ?? $json.message ?? '').replace(/^\\s*\\d{3}\\s*-\\s*/, '').trim()) || 'Unknown error' }}\n<https://www.twain.ai/w|Open Twain workspace>",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.slack_channel }}"
},
"otherOptions": {
"unfurl_links": false,
"unfurl_media": false,
"includeLinkToWorkflow": false
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
},
{
"id": "69d4ba16-d74b-462b-bede-17d1c697c093",
"name": "Slack: High-fit alert",
"type": "n8n-nodes-base.slack",
"onError": "continueRegularOutput",
"position": [
5568,
304
],
"parameters": {
"text": "=*High-fit signup*\n*{{ $('Twain Generate Research').item.json.research?.person?.name || $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email }}* ({{ $('Normalize Signup').item.json.email }}){{ ([$('Twain Generate Research').item.json.research?.person?.title, $('Twain Generate Research').item.json.research?.company?.name].filter(Boolean).join(' @ ')) ? ' \u00b7 ' + ([$('Twain Generate Research').item.json.research?.person?.title, $('Twain Generate Research').item.json.research?.company?.name].filter(Boolean).join(' @ ')) : '' }}\n{{ (($('Twain Generate Research').item.json?.research?.person?.linkedin_profile_url) || ($('Normalize Signup').item.json.linkedin_url) || '') ? '<' + (($('Twain Generate Research').item.json?.research?.person?.linkedin_profile_url) || ($('Normalize Signup').item.json.linkedin_url)) + '|LinkedIn profile>' : '' }}\n*Tier:* {{ $('Stage B \u2014 Fit Judge').item.json.output?.tier ?? $('Stage B \u2014 Fit Judge').item.json.tier ?? 'Unknown' }}\n*Fit:* {{ $('Stage B \u2014 Fit Judge').item.json.output?.summary ?? $('Stage B \u2014 Fit Judge').item.json.summary ?? 'No fit summary returned.' }}\n*Why now:*\n{{ $('Twain Generate Research').item.json.research?.expanded_research?.why_now?.markdown || 'No why-now summary returned.' }}\n<{{ $('Twain Generate Research').item.json.link }}|Open lead in Twain>",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.slack_channel }}"
},
"otherOptions": {
"unfurl_links": false,
"unfurl_media": false,
"includeLinkToWorkflow": false
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"notesInFlow": false,
"typeVersion": 2.5
},
{
"id": "63d3862e-16a6-492d-b3f3-cb6a28cb8e2a",
"name": "HubSpot: Upsert Contact",
"type": "n8n-nodes-base.hubspot",
"onError": "continueRegularOutput",
"position": [
5568,
528
],
"parameters": {
"email": "={{ $('Normalize Signup').item.json.email }}",
"options": {},
"authentication": "oAuth2",
"additionalFields": {
"jobTitle": "={{ $('Twain Generate Research').item.json.research.person.title }}",
"websiteUrl": "={{ $('Twain Generate Research').item.json.research.company.website }}",
"companyName": "={{ $('Twain Generate Research').item.json.research.company.name }}",
"linkedinUrl": "={{ $('Twain Generate Research').item.json.research.person.linkedin_profile_url }}",
"customPropertiesUi": {
"customPropertiesValues": [
{
"value": "={{ $('Stage B \u2014 Fit Judge').item.json.output?.tier ?? $('Stage B \u2014 Fit Judge').item.json.tier ?? '' }}",
"property": "twain_fit_tier"
},
{
"value": "={{ $('Stage B \u2014 Fit Judge').item.json.output?.summary ?? $('Stage B \u2014 Fit Judge').item.json.summary ?? '' }}",
"property": "twain_fit_reason"
}
]
}
}
},
"credentials": {
"hubspotOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "f47d6093-1629-4df4-83b8-a4570204acb7",
"name": "Email Owning AE",
"type": "n8n-nodes-base.gmail",
"onError": "continueRegularOutput",
"position": [
5568,
752
],
"parameters": {
"message": "=<p><strong>High-fit signup</strong></p><p><strong>Tier:</strong> {{ $('Stage B \u2014 Fit Judge').item.json.output?.tier ?? $('Stage B \u2014 Fit Judge').item.json.tier ?? 'Unknown' }}</p><p><strong>{{ $('Twain Generate Research').item.json.research.person.name }}</strong>{{ $('Twain Generate Research').item.json.research.person.title ? ' \u2014 ' + $('Twain Generate Research').item.json.research.person.title : '' }}{{ $('Twain Generate Research').item.json.research.company.name ? ' @ ' + $('Twain Generate Research').item.json.research.company.name : '' }}<br/>{{ $('Normalize Signup').item.json.email }}</p><p><strong>Fit summary:</strong> {{ $('Stage B \u2014 Fit Judge').item.json.output?.summary ?? $('Stage B \u2014 Fit Judge').item.json.summary ?? 'No fit summary returned.' }}</p><p><a href=\"{{ $('Twain Generate Research').item.json.link }}\">Open in Twain</a></p>",
"options": {},
"subject": "=High-fit signup: {{ $('Twain Generate Research').item.json.research.person.name }} ({{ $('Twain Generate Research').item.json.research.company.name }}) \u2014 {{ $('Stage B \u2014 Fit Judge').item.json.output?.tier ?? $('Stage B \u2014 Fit Judge').item.json.tier ?? 'Unknown' }}",
"resource": "draft"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "c9efb69f-40a8-47d1-a5ea-5d0d3c29fa1c",
"name": "Dedupe New Signups",
"type": "n8n-nodes-base.removeDuplicates",
"position": [
1392,
528
],
"parameters": {
"options": {
"scope": "node",
"historySize": 100000
},
"operation": "removeItemsSeenInPreviousExecutions",
"dedupeValue": "={{ $json.uid }}"
},
"typeVersion": 2
},
{
"id": "40137c3e-58b3-4e19-b5a7-dd652841f51a",
"name": "Schedule: BigQuery backfill",
"type": "n8n-nodes-base.scheduleTrigger",
"disabled": true,
"position": [
240,
704
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.3
},
{
"id": "de834cef-8461-456a-85b1-d13e50512155",
"name": "BigQuery: New signups",
"type": "n8n-nodes-base.googleBigQuery",
"disabled": true,
"position": [
624,
960
],
"parameters": {
"options": {},
"sqlQuery": "-- Backfill: new signups since last run. Alias columns to uid/email/displayName/signup_ts.\nSELECT uid, email, display_name AS displayName, signup_ts\nFROM `your_dataset.signups`\nWHERE signup_ts > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)\nORDER BY signup_ts",
"projectId": {
"__rl": true,
"mode": "id",
"value": "REPLACE_WITH_GCP_PROJECT_ID"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "57cf8da0-6a06-4d71-b738-2a77db5dc5e3",
"name": "Schedule: Firestore poll",
"type": "n8n-nodes-base.scheduleTrigger",
"disabled": true,
"position": [
240,
864
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 15
}
]
}
},
"typeVersion": 1.3
},
{
"id": "620d442a-5979-4807-b653-b866e197c1ec",
"name": "Firestore: New customers",
"type": "n8n-nodes-base.googleFirebaseCloudFirestore",
"disabled": true,
"position": [
624,
1184
],
"parameters": {
"query": "{\n \"from\": [{ \"collectionId\": \"customers\" }],\n \"orderBy\": [{ \"field\": { \"fieldPath\": \"created_at\" }, \"direction\": \"DESCENDING\" }],\n \"limit\": 50\n}",
"operation": "query",
"projectId": "REPLACE_WITH_FIREBASE_PROJECT_ID",
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.1
},
{
"id": "bc64fcb1-8681-4bfa-bb8c-3c36361ee5ad",
"name": "Slack: Disqualified",
"type": "n8n-nodes-base.slack",
"onError": "continueRegularOutput",
"position": [
4400,
528
],
"parameters": {
"text": "=:no_entry: *Signup disqualified at Stage A*\n*{{ $('Twain Generate Research').item.json.research?.person?.name || $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email }}* ({{ $('Normalize Signup').item.json.email }})\n{{ (($('Twain Generate Research').item.json?.research?.person?.linkedin_profile_url) || ($('Normalize Signup').item.json.linkedin_url) || '') ? '<' + (($('Twain Generate Research').item.json?.research?.person?.linkedin_profile_url) || ($('Normalize Signup').item.json.linkedin_url)) + '|LinkedIn profile>' : '' }}\n<{{ $('Twain Generate Research').item.json.link }}|Open lead in Twain>",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.slack_channel }}"
},
"otherOptions": {
"unfurl_links": false,
"unfurl_media": false,
"includeLinkToWorkflow": false
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
},
{
"id": "fe544299-13dc-4a0e-b658-d612eefec88e",
"name": "Slack: Low fit (C)",
"type": "n8n-nodes-base.slack",
"onError": "continueRegularOutput",
"position": [
5184,
752
],
"parameters": {
"text": "=:heavy_multiplication_x: *Not a fit (NO FIT)*\n*{{ $('Twain Generate Research').item.json.research?.person?.name || $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email }}* ({{ $('Normalize Signup').item.json.email }}){{ ([$('Twain Generate Research').item.json.research?.person?.title, $('Twain Generate Research').item.json.research?.company?.name].filter(Boolean).join(' @ ')) ? ' \u00b7 ' + ([$('Twain Generate Research').item.json.research?.person?.title, $('Twain Generate Research').item.json.research?.company?.name].filter(Boolean).join(' @ ')) : '' }}\n{{ (($('Twain Generate Research').item.json?.research?.person?.linkedin_profile_url) || ($('Normalize Signup').item.json.linkedin_url) || '') ? '<' + (($('Twain Generate Research').item.json?.research?.person?.linkedin_profile_url) || ($('Normalize Signup').item.json.linkedin_url)) + '|LinkedIn profile>' : '' }}\n*Tier:* {{ $('Stage B \u2014 Fit Judge').item.json.output?.tier ?? $('Stage B \u2014 Fit Judge').item.json.tier ?? 'Unknown' }}\n*Fit:* {{ $('Stage B \u2014 Fit Judge').item.json.output?.summary ?? $('Stage B \u2014 Fit Judge').item.json.summary ?? 'No fit summary returned.' }}\n<{{ $('Twain Generate Research').item.json.link }}|Open lead in Twain>",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.slack_channel }}"
},
"otherOptions": {
"unfurl_links": false,
"unfurl_media": false,
"includeLinkToWorkflow": false
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
},
{
"id": "ba507a33-13dc-4008-a4e8-2f918d80affa",
"name": "Normalize Signup",
"type": "n8n-nodes-base.set",
"position": [
1008,
528
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "uid",
"name": "uid",
"type": "string",
"value": "={{ $json.body?.uid ?? $json.uid }}"
},
{
"id": "email",
"name": "email",
"type": "string",
"value": "={{ ($json.body?.email ?? $json.email ?? \"\").toLowerCase().trim() }}"
},
{
"id": "displayName",
"name": "displayName",
"type": "string",
"value": "={{ $json.body?.displayName ?? $json.displayName ?? \"\" }}"
},
{
"id": "signup_ts",
"name": "signup_ts",
"type": "string",
"value": "={{ $json.body?.signup_ts ?? $json.signup_ts ?? $now.toISO() }}"
},
{
"id": "campaign_id",
"name": "campaign_id",
"type": "string",
"value": "={{ $(\"Settings\").item.json.campaign_id }}"
},
{
"id": "linkedin_url",
"name": "linkedin_url",
"type": "string",
"value": "={{ ($json.body?.linkedin_url ?? $json.body?.linkedin_profile_url ?? $json.linkedin_url ?? $json.linkedin_profile_url ?? \"\").trim() }}"
},
{
"id": "email_domain",
"name": "email_domain",
"type": "string",
"value": "={{ ((($json.body?.email ?? $json.email ?? \"\").toLowerCase().trim()).split(\"@\")[1] ?? \"\").toLowerCase() }}"
},
{
"id": "is_personal_email",
"name": "is_personal_email",
"type": "boolean",
"value": "={{ [\"gmail.com\",\"googlemail.com\",\"outlook.com\",\"hotmail.com\",\"live.com\",\"msn.com\",\"yahoo.com\",\"ymail.com\",\"icloud.com\",\"me.com\",\"mac.com\",\"proton.me\",\"protonmail.com\",\"aol.com\",\"gmx.com\",\"gmx.net\",\"zoho.com\",\"pm.me\",\"mail.com\",\"yandex.com\"].includes(((($json.body?.email ?? $json.email ?? \"\").toLowerCase().trim()).split(\"@\")[1] ?? \"\").toLowerCase()) }}"
},
{
"id": "has_linkedin_url",
"name": "has_linkedin_url",
"type": "boolean",
"value": "={{ Boolean(($json.body?.linkedin_url ?? $json.body?.linkedin_profile_url ?? $json.linkedin_url ?? $json.linkedin_profile_url ?? \"\").trim()) }}"
},
{
"id": "has_work_email",
"name": "has_work_email",
"type": "boolean",
"value": "={{ Boolean((($json.body?.email ?? $json.email ?? \"\").toLowerCase().trim())) && ![\"gmail.com\",\"googlemail.com\",\"outlook.com\",\"hotmail.com\",\"live.com\",\"msn.com\",\"yahoo.com\",\"ymail.com\",\"icloud.com\",\"me.com\",\"mac.com\",\"proton.me\",\"protonmail.com\",\"aol.com\",\"gmx.com\",\"gmx.net\",\"zoho.com\",\"pm.me\",\"mail.com\",\"yandex.com\"].includes((((($json.body?.email ?? $json.email ?? \"\").toLowerCase().trim()).split(\"@\")[1] ?? \"\").toLowerCase())) }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "21178752-fe72-4012-a3cf-feb6390eef8d",
"name": "Needs LinkedIn Lookup?",
"type": "n8n-nodes-base.if",
"position": [
1760,
528
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "needs-li-1",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ !$('Normalize Signup').item.json.has_linkedin_url && Boolean($('Normalize Signup').item.json.email) }}",
"rightValue": ""
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.3
},
{
"id": "23ac4183-d3c6-4d8e-8a22-77025934344b",
"name": "LinkedIn Lookup \u2014 Findymail",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
2144,
304
],
"parameters": {
"url": "https://app.findymail.com/api/search/reverse-email",
"method": "POST",
"options": {
"timeout": 30000
},
"jsonBody": "={{ { email: $('Normalize Signup').item.json.email, with_profile: true } }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth"
},
"credentials": {
"httpBearerAuth": {
"name": "<your credential>"
},
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "2e8c96f2-7a78-4849-90ae-43c8115954e5",
"name": "Apply Resolved LinkedIn",
"type": "n8n-nodes-base.code",
"position": [
2528,
304
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Resolve LinkedIn URL from the provider response.\n// Adjust the field below to match YOUR provider's response shape.\nconst resp = $json || {};\nconst linkedin_url = resp.linkedin_url ?? resp.linkedin_profile_url ?? resp.url ?? resp.contact?.linkedin_url ?? resp.profile?.linkedin_url ?? resp.person?.linkedin_url ?? resp.data?.linkedin_url ?? resp.response?.linkedin_url ?? \"\";\n\n// Carry forward all normalized fields, overriding only the LinkedIn ones.\nconst n = $('Normalize Signup').item.json;\nreturn {\n ...n,\n linkedin_url: linkedin_url,\n has_linkedin_url: Boolean(linkedin_url)\n};\n"
},
"typeVersion": 2
},
{
"id": "99a2b760-f06b-4e79-a21a-9efcf97f1a31",
"name": "Resolved Contact",
"type": "n8n-nodes-base.merge",
"position": [
2912,
528
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "f5f07945-95b6-4181-b5c5-9be06207d7e4",
"name": "LinkedIn Lookup \u2014 Apollo",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"disabled": true,
"position": [
2144,
80
],
"parameters": {
"url": "https://api.apollo.io/api/v1/people/match",
"method": "POST",
"options": {
"timeout": 30000
},
"jsonBody": "={{ { email: $('Normalize Signup').item.json.email } }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"typeVersion": 4.4
},
{
"id": "5a14c68c-da13-4546-abdf-d3d4f988ea88",
"name": "LinkedIn Lookup \u2014 People Data Labs",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"disabled": true,
"position": [
2144,
-144
],
"parameters": {
"url": "=https://api.peopledatalabs.com/v5/person/enrich?email={{ encodeURIComponent($('Normalize Signup').item.json.email) }}",
"options": {
"timeout": 30000
},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"typeVersion": 4.4
},
{
"id": "627f1cca-fbda-4f5f-af51-f47dd25c9a41",
"name": "LinkedIn Lookup \u2014 Proxycurl",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"disabled": true,
"position": [
2144,
-368
],
"parameters": {
"url": "=https://nubela.co/proxycurl/api/linkedin/profile/resolve/email?email={{ encodeURIComponent($('Normalize Signup').item.json.email) }}",
"options": {
"timeout": 30000
},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"typeVersion": 4.4
},
{
"id": "c70fb2d6-7a66-493f-b032-02f45bbe5bc9",
"name": "Sticky: Section 1 Intake",
"type": "n8n-nodes-base.stickyNote",
"position": [
144,
368
],
"parameters": {
"color": 7,
"width": 1520,
"height": 1000,
"content": "### 1 \u00b7 Intake & dedupe\n\nEach new signup arrives via the **webhook** (or an optional BigQuery / Firestore backfill) and is normalized to a common shape. Duplicate `uid`s are then dropped, so every person is processed only once."
},
"typeVersion": 1
},
{
"id": "4d47cca4-066e-48a4-9ae5-8b43665c4ed3",
"name": "Sticky: Section 2 Resolve",
"type": "n8n-nodes-base.stickyNote",
"position": [
1712,
-464
],
"parameters": {
"color": 7,
"width": 1400,
"height": 1180,
"content": "### 2 \u00b7 Resolve contact\n\nSignups on a **personal email** get a LinkedIn / work-email lookup so Twain has a real person to research. Work-email signups skip this. Only one lookup provider is needed."
},
"typeVersion": 1
},
{
"id": "dee205d4-56c0-4c9e-b82e-6ac3a3a6d60a",
"name": "Sticky: Section 3 Qualify",
"type": "n8n-nodes-base.stickyNote",
"position": [
3120,
400
],
"parameters": {
"color": 7,
"width": 1580,
"height": 616,
"content": "### 3 \u00b7 Qualify \u2014 Twain research + 2-stage judge\n\n**The core.** Twain Deep Research enriches the contact, a fast **Stage A** gate hard-disqualifies obvious no-gos, and **Stage B** (an LLM fit judge) scores the rest **A / B / C** with a reason \u2014 fewer LLM calls, grounded decisions."
},
"typeVersion": 1
},
{
"id": "e100f989-f677-4bfc-9bc0-ac2b896f981a",
"name": "Sticky: Section 4 Route",
"type": "n8n-nodes-base.stickyNote",
"position": [
4768,
208
],
"parameters": {
"color": 7,
"width": 1120,
"height": 740,
"content": "### 4 \u00b7 Route the decision\n\n**High fit (A / B)** \u2192 Slack alert + HubSpot upsert + email the owning AE.\n**Low fit (C)**, **disqualified**, and **errors** are posted to Slack \u2014 so nothing is silently dropped."
},
"typeVersion": 1
},
{
"id": "9b17a604-59c1-4f54-af43-93f52b480669",
"name": "Sticky: Try it out",
"type": "n8n-nodes-base.stickyNote",
"position": [
-976,
-128
],
"parameters": {
"width": 900,
"height": 1880,
"content": "## Try It Out!\n\nThis n8n template turns inbound **free signups** into qualified, routed leads \u2014 automatically. For each new signup it resolves a real work contact, runs **Twain Deep Research**, scores fit with a two-stage judge, and routes the result: high-fit leads to Slack + HubSpot + the owning AE, everyone else to Slack for visibility.\n\n### Who's it for\n\nPLG and sales teams that get inbound free signups and want the good-fit ones in front of a human fast \u2014 while the rest are filtered out, with no manual triage.\n\n### How it works\n\n- **Intake** \u2014 a signup hits the webhook (or an optional BigQuery / Firestore backfill), is normalized, and de-duplicated by `uid`.\n- **Resolve contact** \u2014 personal-email signups get a LinkedIn / work-email lookup so there's a real person to research.\n- **Qualify** \u2014 **Twain Deep Research** turns the bare signup into a real profile (person, role, company, industry, size, buying signals). **Stage A** then hard-disqualifies obvious no-gos and **Stage B** (an LLM judge) scores the rest A / B / C on facts, with a reason.\n- **Route** \u2014 high fit \u2192 Slack alert + HubSpot upsert + email the AE; low-fit, disqualified, and errors \u2192 Slack.\n\n### Why Twain Deep Research\n\nA signup is just an email and maybe a name \u2014 not enough to decide fit or personalize routing. Twain enriches it into a full picture of the person and their company (role, seniority, industry, size, signals), so the judge scores on **real data, not the email domain**, and the AE gets context to act on right away.\n\n### How to set up\n\n- **Add credentials:** Twain (HTTP Header Auth, header `X-Api-Key`), a LinkedIn-lookup provider (Findymail by default), Anthropic, Slack, HubSpot, and Gmail.\n- **Configure:** open the **Settings** node and set `campaign_id`, `slack_channel`, and `ae_email` (the AE inbox to notify).\n- **Tune the judge:** edit the **Stage B \u2014 Fit Judge** prompt \u2014 and the **Stage A** disqualify rules \u2014 to define what a good-fit signup means for your ICP.\n- **Trigger:** POST signups to the **Signup Webhook** as `{ email, displayName, linkedin_url, uid, signup_ts }`.\n\n### Requirements\n\nA **Twain** account + API key and a campaign; a LinkedIn-lookup provider (**Findymail**, Apollo, People Data Labs, or Proxycurl); and **Anthropic**, **Slack**, **HubSpot**, and **Gmail** credentials.\n\n### How to customize\n\n- Swap the lookup provider, or enable a **BigQuery / Firestore** backfill trigger instead of the webhook.\n- Route each tier differently (e.g., separate Slack channels, or auto-create tasks for A-tier).\n\nBuilt with Twain \u2014 https://www.twain.ai"
},
"typeVersion": 1
},
{
"id": "50eb4258-2655-4962-99ac-93ee07633ead",
"name": "Sticky: Alt intake",
"type": "n8n-nodes-base.stickyNote",
"position": [
1008,
816
],
"parameters": {
"color": 7,
"width": 560,
"height": 150,
"content": "**Optional intake** \u2014 enable a **BigQuery** or **Firestore** trigger to pull signups on a schedule instead of (or alongside) the webhook. Disabled by default."
},
"typeVersion": 1
},
{
"id": "8d1bda58-6c09-47bc-9954-d8fc5cc2c9c4",
"name": "Sticky: Alt LinkedIn",
"type": "n8n-nodes-base.stickyNote",
"position": [
1712,
-608
],
"parameters": {
"color": 7,
"width": 1120,
"height": 130,
"content": "**Swap the lookup provider** \u2014 Apollo \u00b7 People Data Labs \u00b7 Proxycurl are pre-wired. Enable one, disable the rest. Only one is needed."
},
"typeVersion": 1
},
{
"id": "12321568-0557-4181-8f77-c1ef7394c68f",
"name": "Sticky: config",
"type": "n8n-nodes-base.stickyNote",
"position": [
560,
368
],
"parameters": {
"color": 7,
"width": 560,
"height": 150,
"content": "\u2699\ufe0f **Config lives here** \u2014 set `campaign_id`, `slack_channel`, and `ae_email` in this **Settings** node. Every downstream node reads from it."
},
"typeVersion": 1
},
{
"id": "bc8e6486-ad02-4939-8cc8-6ff8c6130fdd",
"name": "Settings",
"type": "n8n-nodes-base.set",
"position": [
624,
528
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "campaign_id",
"name": "campaign_id",
"type": "string",
"value": "YOUR_TWAIN_CAMPAIGN_ID"
},
{
"id": "slack_channel",
"name": "slack_channel",
"type": "string",
"value": "signups"
},
{
"id": "ae_email",
"name": "ae_email",
"type": "string",
"value": "user@example.com"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "47c668d5-6405-4f76-b6b8-92995f55805a",
"name": "Sticky: why-research",
"type": "n8n-nodes-base.stickyNote",
"position": [
3120,
208
],
"parameters": {
"color": 7,
"width": 772,
"height": 170,
"content": "\ud83d\udd0e **Why Twain Deep Research?** A bare signup \u2014 an email, maybe a name \u2014 isn't enough to judge fit. Twain enriches it into a real profile: person, role & seniority, company, industry, size, and buying signals \u2014 so the judge decides on **facts, not the email domain**, and the AE gets full context to act on."
},
"typeVersion": 1
},
{
"id": "120c1466-ef64-4c23-9b6d-bd2ea526ed18",
"name": "Sticky: judge",
"type": "n8n-nodes-base.stickyNote",
"position": [
3920,
208
],
"parameters": {
"color": 7,
"width": 780,
"height": 170,
"content": "\u270f\ufe0f **Tune the fit judge** \u2014 edit this **Stage B \u2014 Fit Judge** prompt to define your ICP and what makes an A / B / C lead. Also adjust the **Stage A** hard-disqualify rules to match your business."
},
"typeVersion": 1
}
],
"active": true,
"settings": {
"binaryMode": "separate",
"availableInMCP": true,
"executionOrder": "v1"
},
"versionId": "a9cd08fd-baa8-40da-9899-2813eaa6b140",
"nodeGroups": [],
"connections": {
"Settings": {
"main": [
[
{
"node": "Normalize Signup",
"type": "main",
"index": 0
}
]
]
},
"Is High Fit?": {
"main": [
[
{
"node": "High Fit (A/B)",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack: Low fit (C)",
"type": "main",
"index": 0
}
]
]
},
"High Fit (A/B)": {
"main": [
[
{
"node": "Slack: High-fit alert",
"type": "main",
"index": 0
},
{
"node": "HubSpot: Upsert Contact",
"type": "main",
"index": 0
},
{
"node": "Email Owning AE",
"type": "main",
"index": 0
}
]
]
},
"Signup Webhook": {
"main": [
[
{
"node": "Settings",
"type": "main",
"index": 0
}
]
]
},
"Normalize Signup": {
"main": [
[
{
"node": "Dedupe New Signups",
"type": "main",
"index": 0
}
]
]
},
"Resolved Contact": {
"main": [
[
{
"node": "Has usable contact?",
"type": "main",
"index": 0
}
]
]
},
"Claude Sonnet 4.6": {
"ai_languageModel": [
[
{
"node": "Stage B \u2014 Fit Judge",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Dedupe New Signups": {
"main": [
[
{
"node": "Needs LinkedIn Lookup?",
"type": "main",
"index": 0
}
]
]
},
"Fit Verdict Schema": {
"ai_outputParser": [
[
{
"node": "Stage B \u2014 Fit Judge",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Has usable contact?": {
"main": [
[
{
"node": "Twain Generate Research",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack: Personal email",
"type": "main",
"index": 0
}
]
]
},
"BigQuery: New signups": {
"main": [
[
{
"node": "Settings",
"type": "main",
"index": 0
}
]
]
},
"Stage B \u2014 Fit Judge": {
"main": [
[
{
"node": "Is High Fit?",
"type": "main",
"index": 0
}
]
]
},
"Needs LinkedIn Lookup?": {
"main": [
[
{
"node": "LinkedIn Lookup \u2014 Findymail",
"type": "main",
"index": 0
}
],
[
{
"node": "Resolved Contact",
"type": "main",
"index": 1
}
]
]
},
"Apply Resolved LinkedIn": {
"main": [
[
{
"node": "Resolved Contact",
"type": "main",
"index": 0
}
]
]
},
"Twain Generate Research": {
"main": [
[
{
"node": "Stage A \u2014 Hard Disqualify?",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack: Twain error",
"type": "main",
"index": 0
}
]
]
},
"Firestore: New customers": {
"main": [
[
{
"node": "Settings",
"type": "main",
"index": 0
}
]
]
},
"Schedule: Firestore poll": {
"main": [
[
{
"node": "Firestore: New customers",
"type": "main",
"index": 0
}
]
]
},
"Schedule: BigQuery backfill": {
"main": [
[
{
"node": "BigQuery: New signups",
"type": "main",
"index": 0
}
]
]
},
"Stage A \u2014 Hard Disqualify?": {
"main": [
[
{
"node": "Slack: Disqualified",
"type": "main",
"index": 0
}
],
[
{
"node": "Stage B \u2014 Fit Judge",
"type": "main",
"index": 0
}
]
]
},
"LinkedIn Lookup \u2014 Findymail": {
"main": [
[
{
"node": "Apply Resolved LinkedIn",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
anthropicApigmailOAuth2googleApihttpBearerAuthhttpHeaderAuthhubspotOAuth2ApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow receives free-signup events via webhook, enriches the contact with Twain research (and optional Findymail LinkedIn lookup), uses Anthropic Claude to score ICP fit, then routes outcomes to Slack and, for high-fit leads, to HubSpot and Gmail. Receives a POST webhook…
Source: https://n8n.io/workflows/16782/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This template uses the Apify LinkedIn Profile Scraper, which is a community node only available in self-hosted n8n installations. The LinkedIn scraping step is optional and can be removed for n8n Clou
Fully automates your service order pipeline from incoming booking to supplier confirmation — with built-in SLA enforcement and automatic escalation if a supplier goes silent. 📥 Receives orders via web
My workflow 250630. Uses httpRequest, lmChatOpenAi, outputParserStructured, gmail. Event-driven trigger; 37 nodes.
This workflow automates end-to-end contract analysis using AI. It extracts clauses, evaluates risks, tracks obligations, and generates an executive summary from uploaded contracts.
This workflow receives a meeting voice recording via webhook, transcribes it with Deepgram, and uses Google Gemini to extract client details and next-step tasks. It then updates GoHighLevel, posts a t