All Projects

Recruiting Agency Outreach Automation

Recruiting Agency Outreach Automation

A mid-size recruiting agency specializing in tech and finance placements had 4 recruiters spending 6+ hours daily on manual sourcing tasks: searching LinkedIn, copying profiles into Bullhorn, writing personalized outreach emails, and tracking follow-ups in spreadsheets.

Response rates on manual outreach were around 12%. Recruiters had no time to actually work the pipeline because they were buried in sourcing logistics.

We replaced the sourcing and outreach layer with an n8n pipeline that runs on a daily schedule.

The Workflow

Trigger: Daily Schedule (7:00 AM)

The workflow fires automatically each morning. It processes the agency's active job orders and the previous day's new candidate additions to the sourcing queue.


Stage 1: Source Open Roles

Node 1 - Bullhorn API: Fetch Active Job Orders Pulls all active job orders from Bullhorn where status = "Accepting Candidates". Returns job title, required skills, experience range, location, and assigned recruiter.

Node 2 - Split In Batches Processes each job order in a separate branch. Prevents rate limit issues when running multiple roles simultaneously.


Stage 2: Candidate Sourcing Queue

Node 3 - Airtable: Fetch Sourcing Queue Recruiters add LinkedIn profile URLs to an Airtable "Sourcing Queue" throughout the day. The workflow reads all unprocessed rows (status = "Pending").

Node 4 - HTTP Request → LinkedIn Profile Scraper (Apify) Submits each LinkedIn URL to the Apify LinkedIn scraper. Returns: current title, company, location, years of experience, skills list, education, and connection degree.


Stage 3: Contact Enrichment

Node 5 - Hunter.io: Find Email Attempts to find the candidate's work email using name + company domain. Returns email with confidence score.

Node 6 - Clearbit: Person Enrichment Enriches the profile with additional data: past employers, tech stack signals (from job history descriptions), GitHub activity indicator.

Node 7 - IF Node: Email Found?

  • Email found (confidence > 70%) → continues to scoring
  • No email → routes to LinkedIn InMail branch (Node 7b)

Stage 4: Fit Scoring

Node 8 - Code Node: Fit Scoring Engine

function scoreCandidate(candidate, jobOrder) {
  let score = 0;

  // Skills overlap
  const requiredSkills = jobOrder.skills.map(s => s.toLowerCase());
  const candidateSkills = candidate.skills.map(s => s.toLowerCase());
  const skillMatch = requiredSkills.filter(s => candidateSkills.includes(s));
  score += (skillMatch.length / requiredSkills.length) * 40;

  // Experience range
  const exp = candidate.yearsExperience;
  const [minExp, maxExp] = jobOrder.experienceRange;
  if (exp >= minExp && exp <= maxExp) score += 25;
  else if (exp >= minExp - 1) score += 12;

  // Location
  if (candidate.location === jobOrder.location) score += 20;
  else if (candidate.location === jobOrder.remoteEligible) score += 15;

  // Current company signal (competitor or target company list)
  if (jobOrder.targetCompanies.includes(candidate.currentCompany)) score += 15;

  return {
    score: Math.round(score),
    tier: score >= 70 ? 'A' : score >= 45 ? 'B' : 'C',
    matchedSkills: skillMatch
  };
}

Node 9 - Switch: Route by Tier

  • Tier A → priority outreach sequence (same day)
  • Tier B → standard outreach sequence
  • Tier C → archive in Bullhorn, no outreach

Stage 5: Bullhorn CRM Sync

Node 10 - Bullhorn: Create/Update Candidate Upserts the candidate record: profile data, enriched email, fit score, matched skills, sourcing date, assigned job order, and recruiter owner.

Node 11 - Bullhorn: Create Submission Links the candidate to the relevant job order as a submission with status "Sourced - Pending Outreach".


Stage 6: Personalized Outreach

Node 12 - OpenAI: Generate Outreach Email

Uses GPT-4o to generate a personalized first-touch email:

System: You are a recruiter writing a brief, direct outreach email.
Never use "I hope this finds you well". No fluff. 2–3 sentences max.

User: Write an outreach email for:
- Candidate: {name}, currently {title} at {company}
- Role: {jobTitle} at {clientName}
- Matched skills: {matchedSkills}
- Key selling point: {jobOrder.keySellingPoint}

Node 13 - Gmail: Send Outreach (Day 1) Sends the generated email from the assigned recruiter's Gmail account using OAuth. Subject line: {jobTitle} opportunity for {clientName}.

Node 14 - Airtable: Log Outreach Records: candidate, job order, email sent, timestamp, outreach tier. Used for response rate tracking.


Stage 7: Follow-up Sequence

Node 15 - n8n Schedule: Day 4 Follow-up If no reply detected (Gmail label check), sends a short follow-up:

"Just checking if you saw my note last week. Happy to share more details if timing is right."

Node 16 - n8n Schedule: Day 10 Final Touch Final touch focusing on the role's flexibility/compensation if the candidate is Tier A:

"Last note from me on this. The team has flexibility on [remote/comp]. Worth a 15-min call?"

Node 17 - Gmail: Reply Detection Monitors the recruiter's inbox for replies matching the candidate's email. On reply: updates Bullhorn status to "Responded", removes from follow-up sequence, sends Slack alert to recruiter.


Results

  • 6 hrs/day of manual sourcing work replaced
  • Outreach response rate: 12% → 28% (personalized AI-generated emails outperform templates)
  • 94 candidates/week processed on average (was 30–35 manually)
  • Bullhorn data quality improved: all records have email, fit score, and matched skills populated

Stack

LayerTool
Automationn8n (self-hosted)
ATS/CRMBullhorn
Sourcing QueueAirtable
Profile ScrapingApify
Email FindingHunter.io
EnrichmentClearbit
PersonalizationOpenAI GPT-4o
EmailGmail API (OAuth per recruiter)
AlertsSlack

My Role

  • Ran a workflow audit with the recruiting team and documented every manual step across 4 recruiters
  • Built the fit scoring model using 6 months of historical placement data to weight the signals
  • Set up individual Gmail OAuth connections per recruiter so outreach comes from their actual accounts
  • Built the reply detection and sequence cancellation logic
  • Integrated Apify scraper with rate-limit handling and fallback to manual queue on failures
  • Delivered with full runbook documentation so the team can modify job order criteria without touching n8n