Qualify Every Signup in Seconds With Twain and n8n

Mohamed ChahinJune 30, 20268 min read

Stop hand-triaging signups. Twain researches each new signup in ~20 seconds, scores it Tier 1 / Tier 2 / No Fit against your ICP, and routes only real fits to Slack and HubSpot with full context.

Your signups are not the problem. Knowing which ones to drop everything for — that is the problem.

Every inbound signup is a hand raise. But raw signups arrive flat: a real RevOps leader at a 200-person SaaS lands in the same queue as a student, a competitor poking around, and someone using a personal Gmail to kick the tires. Until someone manually digs in, every signup looks identical. So the good ones wait.

This workflow makes that first pass for you. A signup comes in, Twain researches the person and their company in real time, scores fit against your ICP into Tier 1 / Tier 2 / No Fit, and n8n routes only the genuine fits to Slack and HubSpot — with the who, the why, and the why-now already attached. Your AEs stop triaging and start replying.

The signup triage tax

Doing this by hand is slow, and the cost is hidden because it is spread across the whole team.

To qualify one signup properly, a human opens the CRM, searches LinkedIn, reads the company site, and forms a judgment on fit. Done well, that is roughly 15-20 minutes per signup. Multiply that across a busy inbound week and qualification quietly becomes a full part-time job.

So teams cut corners. They skim the email domain and guess. They lean on stale CRM fields that were last touched months ago. They batch-review signups once a day — by which point the hottest lead has already cooled, or signed with someone faster. Speed-to-lead is one of the most reliable predictors of conversion, and manual triage is where it goes to die.

The result is a funnel that treats a six-figure account and a tire-kicker exactly the same until a human gets around to telling them apart.

What you get

This workflow flips the default. Instead of every signup waiting on a human, every signup is researched and scored the instant it arrives — and only the ones worth a human's time reach a human.

  • Real fits only. Tier 1 and Tier 2 signups get pushed to your team. Personal-inbox-only signups, off-ICP roles, and incomplete profiles are filtered out before they ever ping anyone.
  • Fresh research, not stale CRM. Twain researches the lead live against the web and LinkedIn at the moment of signup, so you act on who they are today — not a CRM field from last quarter.
  • Context, not just a name. Each alert carries the person, their title, their company, the fit tier, the one-line reason they matter, and a why-now summary. No tab-hopping required.
  • Faster speed-to-lead. The hot accounts surface in seconds, while intent is still warm, so an AE can reply while the signup is still in the product.
  • Native push to your stack. Fits flow straight into Slack and HubSpot — fit tier and reason written as CRM fields you can route, segment, and report on.
  • Zero manual triage. Nobody opens the CRM to sort signups. The team spends its attention on replies, not on sorting.

For Demand-Gen and RevOps, the ROI is direct: you reclaim the hours that went into manual qualification, and you raise the conversion rate on the leads that mattered by getting to them first.

The numbers

The case is simple when you put the manual path and the automated path side by side.

  • Manual: ~15-20 minutes of research and scoring per signup, done inconsistently, often hours or a day late.
  • Twain: research completed in roughly 20 seconds, scored against your ICP, at every signup, around the clock — with no one in the loop until a real fit appears.

The point is not just that it is faster. It is that it is consistent and complete. Every single signup gets the same thorough first pass that a careful human would do on their best day, and your team only spends time on the ones that earn it.

We are not claiming this replaces an AE's judgment on a live deal. It replaces the tedious, repetitive triage that happens before the AE ever should have been involved — and it gets it done before the lead goes cold.

How the tiering works

Twain does not just enrich a record and hand you raw data to interpret. It judges fit against your actual ICP and returns a verdict you can route on.

When a signup comes in, Twain researches the person and company, then a fit judge scores the result into three buckets and returns a short analysis, a summary, and a tier:

  • Tier 1 — strong fit. Core ICP: the role, seniority, and company type all line up with who buys from you. Drop-everything leads.
  • Tier 2 — plausible fit. Adjacent or early-stage accounts worth a look but not an emergency.
  • No Fit. Off-ICP roles, B2C-only, students, job seekers, obvious mismatches. Logged, not escalated.

Two stages keep it sharp. First, obvious disqualifiers — persona mismatches, incomplete profiles — are filtered out fast and cheaply. Then the fit judge handles the real gray area: the maybes that are genuinely hard to read. That middle bucket is where manual triage wastes the most time, and it is exactly where automated, research-backed scoring earns its keep.

The judging logic is grounded in the campaign you set up in Twain, so the tiers reflect your ICP, not a generic template. Tighten the ICP and the tiers tighten with it — no rewiring the workflow.

What lands in Slack

The difference between a noisy alert and a useful one is whether a human has to do more work after reading it.

A plain "new signup: alex@company.com" still forces someone to go research. A Tier 1 alert from this workflow lands ready to act on:

  • The fit tier, up front.
  • The person, their title, and their company.
  • A one-line reason citing the strongest fit signal.
  • A why-now summary pulled from live research.
  • A direct link into Twain for the full lead view.

An AE reads that in a few seconds and replies — no tabs, no CRM dive, no guesswork. That is what turns a signup notification into pipeline.

Import the template

The whole flow ships as a ready-to-import n8n template. Copy the sanitized workflow JSON below — secrets are removed and live IDs are replaced with placeholders. After import, attach your own Slack, HubSpot, Gmail, Anthropic, and Twain credentials, and point the campaign_id at the signup-triage campaign you created in Twain.

{
  "name": "Twain — Qualify & Route Free Signup",
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": true,
    "binaryMode": "separate"
  },
  "nodes": [
    {
      "id": "e5b87045-866c-4179-8f5f-039aa20374be",
      "name": "Signup Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [240, 520],
      "webhookId": "078a34ae-7a0d-47b1-a37d-d2bfca7e670b",
      "parameters": {
        "httpMethod": "POST",
        "path": "signup-lead-qualify",
        "authentication": "headerAuth",
        "options": {}
      }
    },
    {
      "id": "e136bab6-5b4b-44b8-8661-d4eb4847cfad",
      "name": "Has usable contact?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [3280, 520],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "id": "usable-1",
              "leftValue": "={{ Boolean($json.has_work_email) || Boolean($json.has_linkedin_url) }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "id": "3071075f-8355-4380-82c0-baa8e2f57d58",
      "name": "Slack: Personal email",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [3660, 740],
      "onError": "continueRegularOutput",
      "webhookId": "e6b23779-b62c-481c-a400-edd9d05be2b1",
      "parameters": {
        "authentication": "accessToken",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "n8n-workflows"
        },
        "text": "=:no_entry_sign:  *Twain — Qualify & Route Free Signup — skipped*\n\n*{{ $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email || 'Unknown signup' }}*\n:email:  {{ $('Normalize Signup').item.json.email || 'No email provided' }}\n\nNo work email, and the LinkedIn lookup didn't return a profile URL for this signup — 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\n<https://www.twain.ai/w|Open Twain workspace>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false
        }
      }
    },
    {
      "id": "57f5a107-40c8-41c7-8df6-18dc5bab8278",
      "name": "Twain Generate Research",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [3660, 520],
      "onError": "continueErrorOutput",
      "parameters": {
        "method": "POST",
        "url": "https://public.api.twain.ai/v2/Generate/Research",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "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} }}",
        "options": {
          "timeout": 180000
        }
      }
    },
    {
      "id": "ac4d8c53-4292-47ba-9cea-e5c59befd797",
      "name": "Stage A — Hard Disqualify?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [4040, 520],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "leftValue": "={{ $('Twain Generate Research').item.json.warnings.categories }}",
              "operator": {
                "type": "array",
                "operation": "contains",
                "rightType": "any"
              },
              "rightValue": "PERSONA_MISMATCH",
              "id": "8e8de89c-59d1-4dff-8271-81a3896c5f54"
            },
            {
              "leftValue": "={{ $('Twain Generate Research').item.json.warnings.categories }}",
              "operator": {
                "type": "array",
                "operation": "contains",
                "rightType": "any"
              },
              "rightValue": "INCOMPLETE_PROFILE",
              "id": "99e07edb-fc06-4ba3-86e2-4995a996d349"
            }
          ],
          "combinator": "or"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "id": "5a8ec23c-c547-489d-9e62-b9f54b76de37",
      "name": "Stage B — Fit Judge",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [4420, 520],
      "parameters": {
        "promptType": "define",
        "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 || '' }} — {{ $('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 || '' }}",
        "hasOutputParser": true,
        "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 — 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 — 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) — 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) — 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 — 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 — 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 — 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\")."
        }
      }
    },
    {
      "id": "0f7239d7-3799-4f65-a99c-9c60271da518",
      "name": "Claude Sonnet 4.6",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.5,
      "position": [4324, 740],
      "parameters": {
        "model": {
          "__rl": true,
          "value": "claude-sonnet-4-6",
          "mode": "id",
          "cachedResultName": "Claude Sonnet 4.6"
        },
        "options": {
          "temperature": 0.1
        }
      }
    },
    {
      "id": "0248db4c-fdf0-477f-8d2d-9ecfd931dd06",
      "name": "Fit Verdict Schema",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [4532, 740],
      "parameters": {
        "jsonSchemaExample": "{ \"analysis\": \"RevOps leader at a Series-C B2B SaaS (~400 FTE, US) running outbound — 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\" }"
      }
    },
    {
      "id": "c5a14b42-2b83-41bf-98f6-e4151bd5d5b0",
      "name": "Is High Fit?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [4800, 520],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "leftValue": "={{ $json.output?.tier ?? $json.tier ?? '' }}",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "rightValue": "TIER 1",
              "id": "4300371c-0841-48d9-8392-39f6f9dd308c"
            },
            {
              "leftValue": "={{ $json.output?.tier ?? $json.tier ?? '' }}",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "rightValue": "TIER 2",
              "id": "cef49b99-1cd3-4b4e-991e-40d38552f542"
            }
          ],
          "combinator": "or"
        },
        "options": {}
      }
    },
    {
      "id": "97d7a1ee-16d0-4b46-8c99-5a95e0459b2a",
      "name": "High Fit (A/B)",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [5180, 520],
      "executeOnce": false,
      "parameters": {}
    },
    {
      "id": "b3aaea39-c00c-4445-8e32-6f31f0d3ae22",
      "name": "Slack: Twain error",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [4040, 740],
      "onError": "continueRegularOutput",
      "webhookId": "e69cec69-3121-4f30-9099-02d9a11e58db",
      "parameters": {
        "authentication": "accessToken",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "n8n-workflows"
        },
        "text": "=:warning:  *Twain research failed*\n\n*{{ $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email || 'Unknown signup' }}*\n:email:  {{ $('Normalize Signup').item.json.email || 'No email provided' }}\n\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\n<https://www.twain.ai/w|Open Twain workspace>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false
        }
      }
    },
    {
      "id": "69d4ba16-d74b-462b-bede-17d1c697c093",
      "name": "Slack: High-fit alert",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [5560, 300],
      "onError": "continueRegularOutput",
      "notesInFlow": false,
      "webhookId": "fbfb4b67-0500-41fd-87ab-8762e30393e4",
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "n8n-workflows",
          "mode": "name"
        },
        "text": "=:fire:  *High-fit signup*\n\n*{{ $('Twain Generate Research').item.json.research?.person?.name || $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email }}*\n{{ [$('Twain Generate Research').item.json.research?.person?.title, $('Twain Generate Research').item.json.research?.company?.name].filter(Boolean).join(' @ ') }}\n\n:email:  {{ $('Normalize Signup').item.json.email }}\n{{ $('Twain Generate Research').item.json.research?.person?.linkedin_profile_url ? ':link:  <' + $('Twain Generate Research').item.json.research.person.linkedin_profile_url + '|LinkedIn profile>' : '' }}\n{{ $('Twain Generate Research').item.json.warnings?.has_warnings ? ':warning:  *Warnings:* ' + (($('Twain Generate Research').item.json.warnings.categories) || []).join(', ') + ($('Twain Generate Research').item.json.warnings.summary ? '  —  ' + $('Twain Generate Research').item.json.warnings.summary : '') : '' }}\n\n*Tier:* {{ $('Stage B — Fit Judge').item.json.output?.tier ?? $('Stage B — Fit Judge').item.json.tier ?? 'Unknown' }}\n*Fit:* {{ $('Stage B — Fit Judge').item.json.output?.summary ?? $('Stage B — Fit Judge').item.json.summary ?? 'No fit summary returned.' }}\n\n*Why now:*\n{{ $('Twain Generate Research').item.json.research?.expanded_research?.why_now?.markdown || 'No why-now summary returned.' }}\n\n<{{ $('Twain Generate Research').item.json.link }}|Open lead in Twain>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false
        },
        "authentication": "accessToken"
      }
    },
    {
      "id": "63d3862e-16a6-492d-b3f3-cb6a28cb8e2a",
      "name": "HubSpot: Upsert Contact",
      "type": "n8n-nodes-base.hubspot",
      "typeVersion": 2.2,
      "position": [5560, 520],
      "onError": "continueRegularOutput",
      "parameters": {
        "authentication": "oAuth2",
        "email": "={{ $('Normalize Signup').item.json.email }}",
        "additionalFields": {
          "companyName": "={{ $('Twain Generate Research').item.json.research.company.name }}",
          "customPropertiesUi": {
            "customPropertiesValues": [
              {
                "property": "twain_fit_tier",
                "value": "={{ $('Stage B — Fit Judge').item.json.output?.tier ?? $('Stage B — Fit Judge').item.json.tier ?? '' }}"
              },
              {
                "property": "twain_fit_reason",
                "value": "={{ $('Stage B — Fit Judge').item.json.output?.summary ?? $('Stage B — Fit Judge').item.json.summary ?? '' }}"
              }
            ]
          },
          "jobTitle": "={{ $('Twain Generate Research').item.json.research.person.title }}",
          "linkedinUrl": "={{ $('Twain Generate Research').item.json.research.person.linkedin_profile_url }}",
          "websiteUrl": "={{ $('Twain Generate Research').item.json.research.company.website }}"
        },
        "options": {}
      }
    },
    {
      "id": "f47d6093-1629-4df4-83b8-a4570204acb7",
      "name": "Email Owning AE",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [5560, 740],
      "onError": "continueRegularOutput",
      "webhookId": "3e673757-abf8-47d9-8405-c6fa906f3067",
      "parameters": {
        "sendTo": "REPLACE_WITH_AE_EMAIL@yourcompany.com",
        "subject": "=High-fit signup: {{ $('Twain Generate Research').item.json.research.person.name }} ({{ $('Twain Generate Research').item.json.research.company.name }}) — {{ $('Stage B — Fit Judge').item.json.output?.tier ?? $('Stage B — Fit Judge').item.json.tier ?? 'Unknown' }}",
        "message": "=<p><strong>High-fit signup</strong></p><p><strong>Tier:</strong> {{ $('Stage B — Fit Judge').item.json.output?.tier ?? $('Stage B — 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 ? ' — ' + $('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 — Fit Judge').item.json.output?.summary ?? $('Stage B — 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": {
          "appendAttribution": false
        }
      }
    },
    {
      "id": "c9efb69f-40a8-47d1-a5ea-5d0d3c29fa1c",
      "name": "Dedupe New Signups",
      "type": "n8n-nodes-base.removeDuplicates",
      "typeVersion": 2,
      "position": [1380, 520],
      "parameters": {
        "operation": "removeItemsSeenInPreviousExecutions",
        "dedupeValue": "={{ $json.uid }}",
        "options": {
          "scope": "node",
          "historySize": 100000
        }
      }
    },
    {
      "id": "40137c3e-58b3-4e19-b5a7-dd652841f51a",
      "name": "Schedule: BigQuery backfill",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [240, 690],
      "disabled": true,
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      }
    },
    {
      "id": "de834cef-8461-456a-85b1-d13e50512155",
      "name": "BigQuery: New signups",
      "type": "n8n-nodes-base.googleBigQuery",
      "typeVersion": 2.1,
      "position": [620, 960],
      "disabled": true,
      "parameters": {
        "authentication": "serviceAccount",
        "projectId": {
          "__rl": true,
          "mode": "id",
          "value": "REPLACE_WITH_GCP_PROJECT_ID"
        },
        "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",
        "options": {}
      }
    },
    {
      "id": "57cf8da0-6a06-4d71-b738-2a77db5dc5e3",
      "name": "Schedule: Firestore poll",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [240, 860],
      "disabled": true,
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      }
    },
    {
      "id": "620d442a-5979-4807-b653-b866e197c1ec",
      "name": "Firestore: New customers",
      "type": "n8n-nodes-base.googleFirebaseCloudFirestore",
      "typeVersion": 1.1,
      "position": [620, 1180],
      "disabled": true,
      "parameters": {
        "authentication": "serviceAccount",
        "operation": "query",
        "projectId": "REPLACE_WITH_FIREBASE_PROJECT_ID",
        "query": "{\n  \"from\": [{ \"collectionId\": \"customers\" }],\n  \"orderBy\": [{ \"field\": { \"fieldPath\": \"created_at\" }, \"direction\": \"DESCENDING\" }],\n  \"limit\": 50\n}"
      }
    },
    {
      "id": "bc64fcb1-8681-4bfa-bb8c-3c36361ee5ad",
      "name": "Slack: Disqualified",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [4420, 740],
      "onError": "continueRegularOutput",
      "webhookId": "84ff7a91-b2e1-44b2-9a8b-e43a47c640e7",
      "parameters": {
        "authentication": "accessToken",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "n8n-workflows"
        },
        "text": "=:no_entry:  *Signup disqualified at Stage A*\n\n*{{ $('Twain Generate Research').item.json.research?.person?.name || $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email }}*\n\n:email:  {{ $('Normalize Signup').item.json.email }}\n\n*Warnings:* `{{ (($('Twain Generate Research').item.json.warnings?.categories) || []).join(', ') || 'No warning categories returned' }}`\n{{ $('Twain Generate Research').item.json.warnings?.summary || '' }}\n\n<{{ $('Twain Generate Research').item.json.link }}|Open lead in Twain>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false
        }
      }
    },
    {
      "id": "fe544299-13dc-4a0e-b658-d612eefec88e",
      "name": "Slack: Low fit (C)",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [5180, 740],
      "onError": "continueRegularOutput",
      "webhookId": "8227c635-e756-46b7-afbb-cc93aa82c1e7",
      "parameters": {
        "authentication": "accessToken",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "n8n-workflows"
        },
        "text": "=:heavy_multiplication_x:  *Not a fit (NO FIT)*\n\n*{{ $('Twain Generate Research').item.json.research?.person?.name || $('Normalize Signup').item.json.displayName || $('Normalize Signup').item.json.email }}*\n{{ [$('Twain Generate Research').item.json.research?.person?.title, $('Twain Generate Research').item.json.research?.company?.name].filter(Boolean).join(' @ ') }}\n\n:email:  {{ $('Normalize Signup').item.json.email }}\n{{ $('Twain Generate Research').item.json.research?.person?.linkedin_profile_url ? ':link:  <' + $('Twain Generate Research').item.json.research.person.linkedin_profile_url + '|LinkedIn profile>' : '' }}\n{{ $('Twain Generate Research').item.json.warnings?.has_warnings ? ':warning:  *Warnings:* ' + (($('Twain Generate Research').item.json.warnings.categories) || []).join(', ') + ($('Twain Generate Research').item.json.warnings.summary ? '  —  ' + $('Twain Generate Research').item.json.warnings.summary : '') : '' }}\n\n*Tier:* {{ $('Stage B — Fit Judge').item.json.output?.tier ?? $('Stage B — Fit Judge').item.json.tier ?? 'Unknown' }}\n*Fit:* {{ $('Stage B — Fit Judge').item.json.output?.summary ?? $('Stage B — Fit Judge').item.json.summary ?? 'No fit summary returned.' }}\n\n<{{ $('Twain Generate Research').item.json.link }}|Open lead in Twain>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false
        }
      }
    },
    {
      "id": "ba507a33-13dc-4008-a4e8-2f918d80affa",
      "name": "Normalize Signup",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [1000, 520],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "uid",
              "name": "uid",
              "value": "={{ $json.body?.uid ?? $json.uid }}",
              "type": "string"
            },
            {
              "id": "email",
              "name": "email",
              "value": "={{ ($json.body?.email ?? $json.email ?? \"\").toLowerCase().trim() }}",
              "type": "string"
            },
            {
              "id": "displayName",
              "name": "displayName",
              "value": "={{ $json.body?.displayName ?? $json.displayName ?? \"\" }}",
              "type": "string"
            },
            {
              "id": "signup_ts",
              "name": "signup_ts",
              "value": "={{ $json.body?.signup_ts ?? $json.signup_ts ?? $now.toISO() }}",
              "type": "string"
            },
            {
              "id": "campaign_id",
              "name": "campaign_id",
              "value": "REPLACE_WITH_SIGNUP_TRIAGE_CAMPAIGN_ID",
              "type": "string"
            },
            {
              "id": "linkedin_url",
              "name": "linkedin_url",
              "value": "={{ ($json.body?.linkedin_url ?? $json.body?.linkedin_profile_url ?? $json.linkedin_url ?? $json.linkedin_profile_url ?? \"\").trim() }}",
              "type": "string"
            },
            {
              "id": "email_domain",
              "name": "email_domain",
              "value": "={{ ((($json.body?.email ?? $json.email ?? \"\").toLowerCase().trim()).split(\"@\")[1] ?? \"\").toLowerCase() }}",
              "type": "string"
            },
            {
              "id": "is_personal_email",
              "name": "is_personal_email",
              "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()) }}",
              "type": "boolean"
            },
            {
              "id": "has_linkedin_url",
              "name": "has_linkedin_url",
              "value": "={{ Boolean(($json.body?.linkedin_url ?? $json.body?.linkedin_profile_url ?? $json.linkedin_url ?? $json.linkedin_profile_url ?? \"\").trim()) }}",
              "type": "boolean"
            },
            {
              "id": "has_work_email",
              "name": "has_work_email",
              "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())) }}",
              "type": "boolean"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "21178752-fe72-4012-a3cf-feb6390eef8d",
      "name": "Needs LinkedIn Lookup?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [1760, 520],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "id": "needs-li-1",
              "leftValue": "={{ !$('Normalize Signup').item.json.has_linkedin_url && Boolean($('Normalize Signup').item.json.email) }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "id": "23ac4183-d3c6-4d8e-8a22-77025934344b",
      "name": "LinkedIn Lookup — Findymail",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [2140, 300],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "https://app.findymail.com/api/search/reverse-email",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { email: $('Normalize Signup').item.json.email, with_profile: true } }}",
        "options": {
          "timeout": 30000
        }
      }
    },
    {
      "id": "2e8c96f2-7a78-4849-90ae-43c8115954e5",
      "name": "Apply Resolved LinkedIn",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [2520, 300],
      "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"
      }
    },
    {
      "id": "99a2b760-f06b-4e79-a21a-9efcf97f1a31",
      "name": "Resolved Contact",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [2900, 520],
      "parameters": {}
    },
    {
      "id": "f5f07945-95b6-4181-b5c5-9be06207d7e4",
      "name": "LinkedIn Lookup — Apollo",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [2140, 80],
      "disabled": true,
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "https://api.apollo.io/api/v1/people/match",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { email: $('Normalize Signup').item.json.email } }}",
        "options": {
          "timeout": 30000
        }
      }
    },
    {
      "id": "5a14c68c-da13-4546-abdf-d3d4f988ea88",
      "name": "LinkedIn Lookup — People Data Labs",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [2140, -140],
      "disabled": true,
      "onError": "continueRegularOutput",
      "parameters": {
        "url": "=https://api.peopledatalabs.com/v5/person/enrich?email={{ encodeURIComponent($('Normalize Signup').item.json.email) }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 30000
        }
      }
    },
    {
      "id": "627f1cca-fbda-4f5f-af51-f47dd25c9a41",
      "name": "LinkedIn Lookup — Proxycurl",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [2140, -360],
      "disabled": true,
      "onError": "continueRegularOutput",
      "parameters": {
        "url": "=https://nubela.co/proxycurl/api/linkedin/profile/resolve/email?email={{ encodeURIComponent($('Normalize Signup').item.json.email) }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 30000
        }
      }
    }
  ],
  "connections": {
    "Signup Webhook": {
      "main": [
        [
          {
            "node": "Normalize Signup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Twain Generate Research": {
      "main": [
        [
          {
            "node": "Stage A — Hard Disqualify?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack: Twain error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage A — Hard Disqualify?": {
      "main": [
        [
          {
            "node": "Slack: Disqualified",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Stage B — Fit Judge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage B — Fit Judge": {
      "main": [
        [
          {
            "node": "Is High Fit?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude Sonnet 4.6": {
      "ai_languageModel": [
        [
          {
            "node": "Stage B — Fit Judge",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Fit Verdict Schema": {
      "ai_outputParser": [
        [
          {
            "node": "Stage B — Fit Judge",
            "type": "ai_outputParser",
            "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
          }
        ]
      ]
    },
    "Schedule: BigQuery backfill": {
      "main": [
        [
          {
            "node": "BigQuery: New signups",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BigQuery: New signups": {
      "main": [
        [
          {
            "node": "Normalize Signup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule: Firestore poll": {
      "main": [
        [
          {
            "node": "Firestore: New customers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Firestore: New customers": {
      "main": [
        [
          {
            "node": "Normalize Signup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Signup": {
      "main": [
        [
          {
            "node": "Dedupe New Signups",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Needs LinkedIn Lookup?": {
      "main": [
        [
          {
            "node": "LinkedIn Lookup — Findymail",
            "type": "main",
            "index": 0
          },
          {
            "node": "LinkedIn Lookup — Apollo",
            "type": "main",
            "index": 0
          },
          {
            "node": "LinkedIn Lookup — People Data Labs",
            "type": "main",
            "index": 0
          },
          {
            "node": "LinkedIn Lookup — Proxycurl",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Resolved Contact",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Apply Resolved LinkedIn": {
      "main": [
        [
          {
            "node": "Resolved Contact",
            "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
          }
        ]
      ]
    },
    "LinkedIn Lookup — Findymail": {
      "main": [
        [
          {
            "node": "Apply Resolved LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LinkedIn Lookup — Apollo": {
      "main": [
        [
          {
            "node": "Apply Resolved LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LinkedIn Lookup — People Data Labs": {
      "main": [
        [
          {
            "node": "Apply Resolved LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LinkedIn Lookup — Proxycurl": {
      "main": [
        [
          {
            "node": "Apply Resolved LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dedupe New Signups": {
      "main": [
        [
          {
            "node": "Needs LinkedIn Lookup?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Resolved Contact": {
      "main": [
        [
          {
            "node": "Has usable contact?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has usable contact?": {
      "main": [
        [
          {
            "node": "Twain Generate Research",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack: Personal email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

After import, point it at your environment: add the Twain API key as an n8n Header Auth credential, reconnect Slack, HubSpot, Gmail, and Anthropic, and replace REPLACE_WITH_SIGNUP_TRIAGE_CAMPAIGN_ID in Normalize Signup with the campaign you created in Twain. The Agent and campaign you set up in Twain are what teach the workflow your ICP — get those right and the tiers come out right. Campaign Brief covers shaping the campaign, and Deep Research covers the research layer this workflow runs on.

Who this is for

This pays off most when the top of the funnel is noisy and a fast human response is worth real money.

  • PLG with a sales-assist motion. Most signups are not buyers, but the ones that are should never sit unnoticed until the next CRM review.
  • Founder-led sales. The founder wants the high-signal accounts in Slack the moment they sign up, with enough context to reply well right away.
  • Demand-Gen and RevOps teams. You finally get fit scoring and reasoning as native CRM fields you can route, segment, and report on — without paying the manual-qualification tax to get them.
  • Multi-source lead capture. App signups, landing pages, webinars, and backfills all get the same first-pass qualification through one workflow.

If your motion is pure self-serve and nobody intends to follow up, this is overkill. But if your team ever asks "which of today's signups should we jump on?", this answers it in seconds — and routes the answer for you.