DEV Community

T.M. Gunderson
T.M. Gunderson

Posted on

I Built a Referral Request Machine That Books 3–5 New Clients a Month (Full n8n Workflow)

I Built a Referral Request Machine That Books 3–5 New Clients a Month (Full n8n Workflow)

Most small business owners know referrals are their best lead source. But they never ask for them. Not because they forget — because asking feels awkward, timing is never right, and tracking who referred whom is a mess.

I built an n8n workflow that handles the entire loop: identifies the right moment to ask, sends a personalized referral request, tracks responses, and routes warm introductions straight to my calendar. It runs on autopilot and books 3–5 new clients a month.

Here's the full breakdown and importable workflow.

Why Referrals Beat Every Other Channel

Harvard Business Review data shows referred customers have a 16% higher lifetime value and 37% higher retention rate than non-referred customers. A Wharton study found that referral leads convert at 4x the rate of paid leads.

For a small business billing $2,000–$10,000 per client, each referral is worth $8,000–$40,000 in lifetime value. Yet most businesses spend $0 on systematic referral generation while dumping money into ads.

The problem isn't that referrals don't work. It's that nobody has a system for asking.

The 4 Triggers That Signal "Ask Now"

Not every client is ready to refer you. Timing matters. These are the signals that indicate a client is in a "referral window":

Trigger What It Means Detection Method
Project delivered Client just saw your value Project status → "Complete" in your tracker
Payment received early Client is happy and engaged Stripe webhook: invoice.payment_succeeded
Positive feedback given Client verbally confirmed satisfaction Email contains "thanks" / "great" / "love" keywords
90 days active Client has enough experience with you Date math on client start date

Any single trigger is worth a soft ask. Two triggers in the same week? That client will probably refer you if you just ask.

The n8n Workflow: Referral Machine

Here's the complete workflow. It checks for referral triggers daily, sends personalized requests, and routes responses to your calendar.

[Daily Cron] → [Check Active Clients]
                     ↓
           [Score Referral Readiness]
                     ↓
        ┌────────────┴────────────┐
    Score ≥ 60%              Score < 60%
        ↓                         ↓
  [Generate Personalized      [Log: "Not yet
   Referral Request"]           ready — check
        ↓                       in 14 days"]
  [Send via Email]
        ↓
  [Wait for Response — 5 days]
        ↓
  ┌────┴────┐
  Responded   No Response
  ↓           ↓
[Route to   [Log: "Follow up
 Calendar]   next quarter"]
Enter fullscreen mode Exit fullscreen mode

Full n8n Workflow JSON

Copy this and import it into n8n (Settings → Import from JSON):

{
  "name": "Referral Machine",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [{ "field": "cronExpression", "expression": "0 10 * * 2" }]
        }
      },
      "id": "cron-trigger",
      "name": "Every Tuesday 10am",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [0, 0]
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": "={{$env.CLIENT_SHEET_ID}}",
        "range": "Clients!A:Z"
      },
      "id": "fetch-clients",
      "name": "Fetch Clients",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [220, 0]
    },
    {
      "parameters": {
        "batchSize": 1,
        "options": {}
      },
      "id": "split-clients",
      "name": "Split Into Clients",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [440, 0]
    },
    {
      "parameters": {
        "model": "gpt-4o-mini",
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "You evaluate whether a small business client is in a good position to give a referral. Score 0-100 based on: project completion (30pts), payment timeliness (25pts), positive feedback received (25pts), relationship length (20pts). Output JSON only."
            },
            {
              "role": "user",
              "content": "=Client: {{ $json.client_name }}\nActive since: {{ $json.start_date }}\nLast project status: {{ $json.last_project_status }}\nLast payment date: {{ $json.last_payment_date }}\nPayment on-time rate: {{ $json.on_time_payment_pct }}%\nFeedback received: {{ $json.positive_feedback }}\nLast feedback date: {{ $json.last_feedback_date }}\nTotal revenue: ${{ $json.total_revenue }}\nIndustry: {{ $json.industry }}\n\nScore their referral readiness 0-100. Return JSON:\n{\n  \"referral_score\": <number>,\n  \"top_trigger\": \"<which signal is strongest>\",\n  \"personalized_angle\": \"<specific reason this client would refer us>\",\n  \"draft_subject\": \"<referral request email subject>\",\n  \"draft_body\": \"<2-3 sentence referral request, warm and specific, not salesy>\"\n}"
            }
          ]
        },
        "options": {
          "temperature": 0.4,
          "responseFormat": "json"
        }
      },
      "id": "ai-score",
      "name": "Score Referral Readiness",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8,
      "position": [660, 0]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftExpression": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "ready",
              "leftValue": "={{ $json.referral_score }}",
              "rightValue": 60,
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "check-score",
      "name": "Score ≥ 60?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.1,
      "position": [880, 0]
    },
    {
      "parameters": {
        "fromEmail": "={{$env.SENDER_EMAIL}}",
        "toEmail": "={{ $json.client_email }}",
        "subject": "={{ $json.draft_subject }}",
        "text": "={{ $json.draft_body }}\n\nIf anyone comes to mind, I'd love an intro — even just a name and I can take it from there.\n\nThanks,\n{{ $env.YOUR_NAME }}"
      },
      "id": "send-referral-request",
      "name": "Send Referral Request",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [1100, -100]
    },
    {
      "parameters": {
        "channel": "={{ $env.SLACK_CHANNEL_ID }}",
        "text": "=📬 *Referral request sent*\n\nClient: {{ $json.client_name }}\nScore: {{ $json.referral_score }}/100\nTrigger: {{ $json.top_trigger }}\nAngle: {{ $json.personalized_angle }}"
      },
      "id": "slack-notify",
      "name": "Slack Notification",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [1320, -100]
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": "={{ $env.CLIENT_SHEET_ID }}",
        "range": "={{ 'Clients!A' + $json.row_number }}",
        "options": {}
      },
      "id": "update-sheet-sent",
      "name": "Update Sheet (Sent)",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [1540, -100]
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": "={{ $env.CLIENT_SHEET_ID }}",
        "range": "={{ 'Clients!A' + $json.row_number }}",
        "options": {}
      },
      "id": "update-sheet-not-ready",
      "name": "Update Sheet (Not Ready)",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [1100, 100]
    }
  ],
  "connections": {
    "Every Tuesday 10am": {
      "main": [[{ "node": "Fetch Clients", "type": "main", "index": 0 }]]
    },
    "Fetch Clients": {
      "main": [[{ "node": "Split Into Clients", "type": "main", "index": 0 }]]
    },
    "Split Into Clients": {
      "main": [[{ "node": "Score Referral Readiness", "type": "main", "index": 0 }]]
    },
    "Score Referral Readiness": {
      "main": [[{ "node": "Score ≥ 60?", "type": "main", "index": 0 }]]
    },
    "Score ≥ 60?": {
      "main": [
        [{ "node": "Send Referral Request", "type": "main", "index": 0 }],
        [{ "node": "Update Sheet (Not Ready)", "type": "main", "index": 0 }]
      ]
    },
    "Send Referral Request": {
      "main": [[{ "node": "Slack Notification", "type": "main", "index": 0 }]]
    },
    "Slack Notification": {
      "main": [[{ "node": "Update Sheet (Sent)", "type": "main", "index": 0 }]]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The Google Sheet Setup

Create a Clients sheet with these columns:

A B C D E F G H I J K L
client_name client_email start_date last_project_status last_payment_date on_time_payment_pct positive_feedback last_feedback_date total_revenue industry referral_score referral_sent_date

Fill in the basics (name, email, start date, revenue, industry). The workflow handles scoring and outreach.

The Scoring Logic Explained

The AI prompt weights four factors:

  1. Project completion (30pts) — A client who just saw you deliver is thinking about how good you are. That's when your name is top of mind.
  2. Payment timeliness (25pts) — Clients who pay on time are happy clients. Late payers are stressed about something.
  3. Positive feedback received (25pts) — If they told you "great job," they've already expressed satisfaction. Now channel it outward.
  4. Relationship length (20pts) — Clients who've been with you 3+ months have enough experience to vouch for you. Under 30 days? Too early.

The 60-point threshold means you need at least two strong signals before you ask. One signal alone usually isn't enough — you want clients who are genuinely enthusiastic, not just "not unhappy."

Response Handling: The Second Workflow

The referral request is step one. Step two is handling the response. Build a separate workflow that:

  1. Monitors inbox for replies to your referral request
  2. Uses AI to classify the reply: "warm intro offered", "maybe later", "no response sentiment", or "interested but needs more info"
  3. Routes warm intros to calendar: Automatically creates a lead entry and sends a booking link
  4. Follows up on "maybe later": Schedules a check-in in 60 days
  5. Logs everything back to your Google Sheet

The classification prompt:

Classify this email reply to a referral request:

Sender: {sender_name}
Original request sent: {request_date}
Reply body: {reply_body}

Return JSON:
{
  "classification": "warm_intro" | "maybe_later" | "not_now" | "needs_info",
  "referred_name": "<name if provided, else null>",
  "referred_company": "<company if provided, else null>",
  "confidence": <0-100>,
  "suggested_next_step": "<specific action>"
}
Enter fullscreen mode Exit fullscreen mode

For warm_intro classifications, create a calendar entry and send yourself a Slack alert with the contact info. For maybe_later, schedule a follow-up for 60 days out. For not_now, log and revisit next quarter.

What This Actually Costs

Running both workflows weekly for 30 clients:

  • GPT-4o-mini calls: 30 clients × 4 weeks × $0.0001 ≈ $0.01/month
  • Email sending: Included in your existing email
  • Google Sheets: Free
  • Slack: You're already paying for it

Total: ~$0.01/month. One referral that closes at $2,000+ makes this the highest-ROI system in your stack.

Why Most Referral Programs Fail

The three mistakes I see repeatedly:

  1. Asking everyone at the same time. ("Hey everyone, refer us!") — This feels like spam. The system above only asks clients who are genuinely ready based on actual behavior data.

  2. Making the ask too complicated. ("Fill out this 10-field form and we'll send them a customized email...") — No. The email should be 2-3 sentences. If they want to refer you, they'll just forward your note or CC you on an intro.

  3. Never following up. You ask once, get a "not right now," and never ask again. The scoring system re-evaluates quarterly, so you catch clients when their situation changes.

The Math That Convinced Me

If you have 20 active clients and this system identifies 5 referral-ready clients per month:

  • Conservative 40% response rate → 2 referrals per month
  • Industry average 50% referral-to-client conversion → 1 new client per month
  • At $3,000 average contract value → $36,000/year in pure referral revenue
  • Cost: $0.12/year in API calls

Even at half that — 0.5 new clients per month — that's still $18,000/year for essentially free.

Customization Tips

  1. Adjust the scoring threshold. If you're getting too many "no" responses, raise it to 70. If you're not asking enough, lower it to 50.
  2. Add industry-specific language. The AI prompt already takes the client's industry, but you can add a sentence like "We've been working with other [industry] businesses on [specific service]" to make it more relevant.
  3. Incentivize (carefully). Some businesses offer a small referral bonus ($50–$100 credit). If you do, mention it in the draft email body. Just don't make the incentive the focus — the personal relationship is what drives quality referrals.
  4. Track referral sources. Add a "referred_by" column to your lead tracking sheet. Over time, this tells you which clients are your best advocates.

Resources


We're building these tools and sharing what we learn. If you're running a small business and want more predictable referral pipelines, follow along at SMB Scale Up.

Top comments (0)