Guide

AI Lead Desk Build Kit (Make/Zapier/n8n)

A copy‑the‑build kit to stand up a zero‑hire Lead Desk: enrich every inbound, score against your ICP JSON, and route to Calendly only if qualified. Includes paste‑ready JSON, a tiny triage prompt, routing configs, nurture emails, run‑log schema, backoff code, and a cost sheet outline.

Copy this build to qualify inbound leads automatically. Enrich the record, score it against your ICP JSON, and route to Calendly only if it clears your threshold. Near‑miss leads get value via a nurture lane; true no‑fits get a polite close. Includes paste‑ready JSON, prompts, configs, and safe ops defaults.

What you’re building: the zero‑hire Lead Desk

You’re wiring an enrich → triage → score → route loop that protects your calendar without adding busywork.

High‑level flow:

  • Capture: Site form (or inbox/DM) → webhook.
  • Enrich: Firmographics/contacts (Clearbit/Dropcontact or your pick).
  • Triage: Tiny LLM prompt returns pass | near_miss | fail + reasons.
  • Score: Deterministic pass/fail via your ICP JSON thresholds.
  • Route:
    • PASS → show Calendly (or send booking link) and assign owner.
    • NEAR‑MISS → add to value lane (email sequence + useful asset).
    • FAIL → graceful “not a fit” reply with alternatives.
  • Log: Emit a run‑log event per step with failure classes.

Wiring blueprints:

  • Make (scenario): Webhooks → HTTP (Clearbit/Dropcontact) → AI (OpenAI/HTTP) → Tools > JSON → Router (3 paths) → HTTP (Calendly/email/CRM) → Data store (run‑logs).
  • Zapier (Zap): Webhooks by Zapier → Webhooks (Clearbit/Dropcontact) → OpenAI (or Webhooks) → Filter + Paths → Gmail/Outlook + CRM → Webhooks (run‑log).
  • n8n (workflow): HTTP Trigger → HTTP Request (enrichment) → OpenAI/HTTP → Function (score JSON) → Switch (paths) → HTTP Request (Calendly/email/CRM) → HTTP Request (log).

ICP rules JSON (paste‑ready)

Use this as your single source of truth for qualification. Keep it short, explicit, and testable.

{
  "version": "1.0.0",
  "hard_disqualifiers": [
    { "field": "email_domain", "operator": "in", "value": ["gmail.com", "yahoo.com", "outlook.com"], "reason": "Free email domain" },
    { "field": "country", "operator": "not_in", "value": ["US", "CA", "UK", "EU"], "reason": "Out of service region" },
    { "field": "industry", "operator": "in", "value": ["B2C e-commerce dropshipping", "crypto", "adult"], "reason": "Outside target industries" }
  ],
  "weights": {
    "employee_count": 0.25,
    "industry_fit": 0.20,
    "title_seniority": 0.20,
    "tech_stack_fit": 0.15,
    "geo_fit": 0.10,
    "inbound_intent": 0.10
  },
  "scoring_rules": [
    { "field": "employee_count", "operator": "between", "value": [10, 500], "score": 1 },
    { "field": "industry", "operator": "in", "value": ["B2B SaaS", "Professional Services", "Healthcare B2B"], "score": 1 },
    { "field": "title_seniority", "operator": "in", "value": ["Founder", "Owner", "VP", "Head"], "score": 1 },
    { "field": "tech_stack", "operator": "contains_any", "value": ["HubSpot", "Salesforce", "Webflow", "Gmail", "Office365"], "score": 1 },
    { "field": "country", "operator": "in", "value": ["US", "CA", "UK", "DE", "FR", "AU"], "score": 1 },
    { "field": "intent", "operator": "in", "value": ["Implementation now", "Book an audit"], "score": 1 }
  ],
  "thresholds": {
    "auto_book_min_score": 0.70,
    "near_miss_floor": 0.50
  },
  "owner_lookup": {
    "precedence": ["domain", "company_name", "crm_account_id"],
    "fallback_owner_id": "OWNER_DEFAULT"
  },
  "notes": "Tune thresholds monthly. Treat hard disqualifiers as final unless overridden via manual review queue."
}

Implementation tips:

  • Keep 3–4 scoring signals you trust; prune anything you can’t enrich reliably.
  • Near‑miss should be generous (keeps optionality via nurture).

LLM triage prompt, token caps, and confidence rubric

This prompt is deliberately short. Require JSON only. Budget tokens so it’s fast and cheap.

Recommended caps (practitioner defaults):

  • Input cap: 900 characters of lead context (≈ 225 tokens).
  • Output cap: 120 tokens.
  • Temperature: 0.0–0.2 for consistency.

Prompt:

You are a lead triage classifier. Using only the provided lead context and ICP rules, decide PASS, NEAR_MISS, or FAIL and explain briefly.

Return JSON with keys: decision ("pass"|"near_miss"|"fail"), reasons (array), confidence (0–1), must_followups (array of 0–2 short questions if NEAR_MISS else []).

Lead context (<=900 chars): {{{LEAD_CONTEXT}}}
ICP summary: {{{ICP_SUMMARY}}}

Confidence rubric:

  • 0.80–1.00: All critical signals present and align with ICP; reasons cite concrete fields.
  • 0.50–0.79: Some ambiguity or missing enrichment; at least one reason references a gap.
  • <0.50: Conflicting or insufficient signals.

Retry/backoff on 429s:

  • Retries: up to 3 with random exponential backoff (base 1s, cap 20s, full jitter).
  • On repeated 429: reduce max_tokens by ~20% and drop non‑essential context.

Output example:

{
  &quot;decision&quot;: &quot;near_miss&quot;,
  &quot;reasons&quot;: [&quot;employee_count below floor (7)&quot;, &quot;title=Founder matches ICP&quot;],
  &quot;confidence&quot;: 0.62,
  &quot;must_followups&quot;: [&quot;Timeline to start?&quot;, &quot;Budget owner?&quot;]
}

Calendly Routing Form + CRM owner lookup + embed

Goal: show booking only to qualified visitors while assigning the right owner.

Routing Form (keep to 3–4 fields):

  • Work email (required)
  • Company website or domain (required)
  • Team size (select: 1–9, 10–49, 50–249, 250–500, 500+)
  • What do you need help with? (select: Audit, Implementation, Support)

Rules (examples — mirror your ICP JSON):

  • If team size ≥ 10 AND need = Implementation → show event type “Discovery – Qualified (30m)”.
  • Else if team size 1–9 OR need = Support → show message + link to resource and “Join waitlist”.

CRM owner lookup:

  • Enable Salesforce or HubSpot lookup to assign based on domain; fallback to default owner if no match.

Embed (example — styling optional):

&lt;div id=&quot;calendly-inline-widget&quot; style=&quot;min-width:320px;height:780px&quot; 
  data-url=&quot;https://calendly.com/yourteam/routing-form?hide_event_type_details=1&amp;background_color=0D1321&amp;text_color=ffffff&amp;primary_color=2FD4EA&quot;&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;https://assets.calendly.com/assets/external/widget.js&quot; async&gt;&lt;/script&gt;

Friction control:

  • Cap total visible fields at 3–4; use enrichment to infer the rest.
  • Pre‑fill email/domain from URL params where possible.
  • Measure: form starts → qualified shows → bookings; watch false negatives via the run‑log.

Nurture lane pack (no‑fit and near‑miss)

Two short sequences you can paste today.

  1. No‑fit (1 email)
  • Subject: We’re not the best fit — here’s something better
  • Body:
Hi [FIRST_NAME] — thanks for reaching out.
Based on what you shared, we’re not the best fit for [NEED/USE CASE]. Rather than waste your time, here are resources that help right away:
• [LINK_1: Practical guide]
• [LINK_2: Template or checklist]
If your situation changes (team ≥ [MIN_EMPLOYEES] and [TECH_STACK] in place), reply to this email and we’ll take another look.
— [SENDER]
  1. Near‑miss (2 emails over 7 days)
  • Email 1 (immediate)
    • Subject: Quick win now + 15‑min audit when ready
    • Body:
Hi [FIRST_NAME], good news: you’re close. Two quick wins to make you a fit fast:
1) Implement [TINY STEP] — tutorial: [LINK]
2) Add [MISSING STACK ITEM] — checklist: [LINK]
When that’s in place, use this link to book: [QUALIFIED_CALENDLY_URL].
— [SENDER]
  • Email 2 (day 7)
    • Subject: Still want the audit?
    • Body: Short reminder + the same booking link.

Assets to prep once:

  • 1-page “Starter Stack” PDF or Notion page.
  • 5–7 minute loom showing the qualified path.
  • Public checklist link you can safely share.

Run‑log webhook schema + failure classes

Log every meaningful step so you can debug and tune thresholds. Emit one event per step to a single webhook (or data store).

Event schema (suggested):

{
  &quot;run_id&quot;: &quot;uuid-v4&quot;,
  &quot;lead_id&quot;: &quot;uuid-or-email-hash&quot;,
  &quot;source&quot;: &quot;webform|inbox|dm|ads&quot;,
  &quot;stack&quot;: &quot;make|zapier|n8n&quot;,
  &quot;timestamp&quot;: &quot;2026-06-12T19:20:30Z&quot;,
  &quot;event&quot;: &quot;enrichment_success|enrichment_miss|llm_triage|score_computed|route_auto_book|route_near_miss|route_disqualified|email_sent|rate_limit_hit|retry_scheduled|error&quot;,
  &quot;payload&quot;: { &quot;redacted_fields&quot;: [&quot;email&quot;], &quot;summary&quot;: &quot;...&quot; },
  &quot;attempt&quot;: 1,
  &quot;duration_ms&quot;: 1420,
  &quot;http_status&quot;: 200,
  &quot;provider&quot;: &quot;clearbit|dropcontact|openai|gmail|graph|calendly&quot;,
  &quot;cost_usd_estimate&quot;: 0.09,
  &quot;failure_class&quot;: null,
  &quot;trace&quot;: { &quot;request_id&quot;: &quot;...&quot;, &quot;retry_after_s&quot;: null }
}

Failure classes (standardize these):

  • ENRICH.NO_MATCH — no firmographic hit.
  • ENRICH.TIMEOUT — enrichment SLA exceeded.
  • LLM.RATE_LIMIT — 429 during triage.
  • LLM.BAD_JSON — non‑parseable output.
  • SCORE.MISSING_FIELD — required key absent.
  • ROUTE.CALENDLY_UNAVAILABLE — embed/API error.
  • EMAIL.SEND_FAILED — provider error.

Storage:

  • Lightweight: Post to a serverless endpoint → append to a log table (SQLite/Postgres/Airtable/Notion DB).
  • Heavyweight: Stream to BigQuery or Snowflake if you’re volume‑heavy.

Tip: Make logs idempotent via run_id + event + attempt.

Rate‑limit backoff snippets (Gmail/Graph/Calendly)

Use exponential backoff with jitter and honor Retry-After headers. Keep retries idempotent.

Node/JS (axios):

async function withBackoff(fn, {max=3, base=1000, cap=20000}={}) {
  for (let i=0; i&lt;=max; i++) {
    try { return await fn(); } 
    catch (e) {
      const status = e?.response?.status;
      const retryAfter = Number(e?.response?.headers[&#39;retry-after&#39;]);
      if (i===max || !(status===429 || status===503)) throw e;
      const sleep = retryAfter ? retryAfter*1000 : Math.min(cap, (base * 2**i) * (0.5 + Math.random()));
      await new Promise(r =&gt; setTimeout(r, sleep));
    }
  }
}

Python (requests):

import time, random, requests

def with_backoff(call, max_retries=3, base=1.0, cap=20.0):
    for i in range(max_retries+1):
        try:
            return call()
        except requests.HTTPError as e:
            status = e.response.status_code if e.response else None
            retry_after = e.response.headers.get(&#39;Retry-After&#39;) if e.response else None
            if i==max_retries or status not in (429, 503):
                raise
            sleep = float(retry_after) if retry_after else min(cap, (base*(2**i)) * (0.5 + random.random()))
            time.sleep(sleep)

Make/Zapier patterns:

  • Keep an attempt counter in a data store.
  • On 429/503, Delay (randomized) → re‑queue the same step.
  • Use idempotency keys when calling mail/cal providers.

Provider notes:

  • Gmail API and Microsoft Graph both return 429/503 under load — back off and retry with jitter.
  • For calendars/booking, expect occasional 429s; avoid fan‑out bursts (queue requests).

Appendix — sample dataset + ops cost estimator

Start with a small, consistent dataset and a simple cost sheet. Replace bracketed values with your numbers.

Sample CSV (3 rows):

email,domain,company,employee_count,industry,country,intent
alex@acme.io,acme.io,Acme,42,B2B SaaS,US,Implementation now
sara@solo.dev,solo.dev,Solo Dev,1,Freelance,UK,General inquiry
lee@contoso.com,contoso.com,Contoso,320,Professional Services,CA,Book an audit

Ops cost estimator (Notion/Sheets columns):

  • Enrichment provider, Enriched? (Y/N), Cost/lead
  • LLM prompt tokens, LLM completion tokens, Model $/1K tokens, LLM cost/lead
  • Sends (email/calendar API ops), $/op, Ops cost/lead
  • Total cost/lead

Formulas:

  • Enrichment (Clearbit Batch): =IF([Enriched?]=&quot;Y&quot;, 0.30, 0)
  • Enrichment (Dropcontact): =[CREDITS_USED] * [CREDIT_PRICE_USD] (Pay‑On‑Success — use your plan’s credit value)
  • LLM: =((PromptTokens + CompletionTokens)/1000) * ModelPricePer1k
  • Total: =Enrichment + LLM + Ops

Guardrails to track weekly:

  • % Qualified show rate (qualified shows ÷ form starts)
  • Booking rate (bookings ÷ qualified shows)
  • False negative rate (manual approvals ÷ near‑miss + fail)
  • Median latency (capture → booking shown)

Tuning loop:

  • If false negatives > 5%, lower auto_book_min_score by 0.05 and/or relax one scoring rule.
  • If calendar quality drops, tighten 1 high‑signal rule (e.g., title_seniority).