C1.Chain Labs
Teardown

$10k in a week: the WhatsApp reactivation agent, taken apart.

We ran our own reactivation agent on Chain Labs' dormant network for 7 days. It closed $10,000 in retainer conversations. Here's the stack, the prompts, the rate-limit math, and the three things that almost sunk it.

Siddharth Rao
Siddharth RaoCo-founder · Chain Labs
·28 Mar 2026·7 min read
WhatsApp reactivation agent teardown — chat bubbles, rising conversion bars, and the $10k outcome

A recruiter's CRM is a graveyard. Three years of candidates, hiring managers, one-off intros. You'll never cold-call them again. You'll never write the follow-up email. The CRM knows this, which is why it ranks them 7/10 and lets you forget.

So we asked an obvious question: can an agent reactivate that graveyard without sounding like an agent? Not "your 73-touch sequence is ready to launch" — actually warm, actually polite, actually on WhatsApp where people still reply.

Short answer: yes. Longer answer: here's what it took.

What we built (the stack)

Nothing exotic. Boring on purpose. An agent that fails gracefully beats a clever one that leaks into your brand.

runtime        Node 20 · TypeScript
orchestration  Inngest (cron + step fns)
model          claude-sonnet-4.5 (tone) + gpt-4.1 (intent)
channel        WhatsApp Business Cloud API
crm            HubSpot (pull) · Notion (state + logs)
guardrails     hand-written regex + LLM judge
cost           $48.12 all-in for the week

The whole thing is ~1,200 lines. We could've used LangGraph or CrewAI. We didn't. On a week-long experiment, any abstraction you don't understand at 2am will bite you at 3am.

The kill-switch matters more than the opener. If you can't politely close a thread, you shouldn't be allowed to open one.

internal review, day 3

The opener was not the hard part

Everybody wants to talk about the first message. It's the easiest thing. Here's what actually went out:

Sample outbound · contact: "Priya · former placement 2023"
Hey Priya — long time. Still at Acme?
hey! yeah still here. why, what's up
Two of our clients are hiring Staff SWEs that match your Go + infra chops. Both fully remote, one's at ~$240k + equity. Worth a 10-min look?

Three things worth noting. It's short. It uses a name we actually know (pulled from the CRM note, not fabricated). And it names one fact the recipient can verify (Go + infra). That's the whole trick. The rest is restraint.

Where it went wrong, round one

The first version of the opener had a sentence like "I wanted to reach out because I think you'd be a great fit..." That sentence, written by any model in 2026, is a signature. Humans don't write that. Recruiters definitely don't. We stripped every construction that read like LinkedIn Recruiter boilerplate. Reply rate jumped from 7% to 16% overnight.

Rate limits are the whole engineering problem

WhatsApp Business's tiered messaging limits are real, and hitting them bans your number for 24h. The messaging tier depends on your quality rating, which depends on block/report rates. Our early instinct was to max throughput. That was wrong.

DaySendsRepliesReply rateQuality
Mon6058.3%MEDIUM
Tue401025.0%HIGH
Wed381436.8%HIGH
Thu521325.0%HIGH
Fri46919.6%HIGH
Sat24520.8%HIGH
Sun24416.7%HIGH

Mon's 60 sends kept us stuck at MEDIUM quality. Tue's cut to 40 recovered it and tripled reply rate, because the agent was now only reaching out to the top-ranked contacts instead of grinding through the list. Slower is better. We added a hard cap.

const DAILY_CAP = 45;
const HOURLY_CAP = 8;
const MIN_GAP_MS = 90_000; // 1.5m between sends

if (sentToday >= DAILY_CAP) return pauseUntil(tomorrow);
if (sentThisHour >= HOURLY_CAP) return pauseFor(15 * 60_000);
if (Date.now() - lastSentAt < MIN_GAP_MS) return pauseFor(90_000);

Reply rate vs. time — the pattern that surprised us

We expected a classic decay: most replies in the first hour, long tail. What we saw instead:

Fig 01 · Reply distribution by hours since send
Most replies landed in the next morning's commute, not the hour you sent.
0–1h8
1–4h12
4–12h7
12–24h (AM)19
24–48h10
48h+4
n = 60 replies from 284 warm-outbounds · WhatsApp · March 20–27, 2026

The biggest bucket is "read in bed, replied on the walk to the office". This changes the agent. Sending between 7pm and 10pm local time did better than sending at 9am. That's the opposite of what cold-email playbooks tell you.

The escalation router (aka: when to hand off)

The agent's job is not to close. The agent's job is to know when it's out of its depth and hand to a human. We scored every inbound reply on two dimensions: intent (interest, question, pass, complaint) and temperature (cool, warm, hot).

Intent × temperature routing matrix — 12 cells showing how agent replies, auto-closes, or escalates to a human
Fig 02 · Every reply fell into one of 12 cells. 4 routed to a human immediately, 4 got an agent response, 4 closed politely without a human ever touching the thread.

The interesting cell was (question, warm) — people asking "what's the role like?" or "is it remote?" The agent handled those because the answers were factually in the JD. We gave it the JDs as retrieved context. It did not improvise.

Every message the agent sends is one you'll have to stand behind. So write the rules like you're hiring a junior who you respect but don't yet trust.

What the agent was not allowed to do

  • Quote specific salaries not already in the CRM
  • Name the client company unless the contact asked directly
  • Respond to anything emotional (layoff grief, job loss, personal news) — those routed to a human, always
  • Use exclamation points (one editorial rule, enforced by regex)
  • Send more than two messages to a silent thread

The kill-switch (the most important part)

When someone says "not interested right now", most sequences treat that as a soft no and queue a follow-up in 60 days. Our agent closes the thread. Permanently. Politely.

Sample close · contact said "not looking, maybe in 6 months"
Totally fair. I'll stop bugging you. If anything changes, you know where to find me — and I'll keep an eye out for the right kind of role before I reach out again.

This cost us two re-engagements we could've otherwise gotten. It earned us exactly zero block-reports in 284 outbounds, which is why we kept sending the next week.

What the money actually looked like

LineCount$
Outbounds284
Replies60
Warm threads (scored hot)18
Scoping calls booked6
Retainers signed2$10,000
API costs (Claude + GPT-4.1)$32.08
WhatsApp Business fees$16.04
Net for the week≈ $9,952

The honest caveat: this was our own network, warmed by relationships we'd already built. A cold list would not do this. A well-tended list of people you've placed, pitched, or chatted with once — that's the list this agent is for.

Three things I'd do differently

  1. Segment sooner. Our first day treated all 284 contacts as equivalent. The ones with "recent mutual LinkedIn interaction" converted 3× the rest. We should've shipped with a tier system, not bolted one on Wednesday.
  2. Let the agent ask a dumb question. Our best conversations started when the agent said "are you still doing Go / infra, or have you drifted?" That one question beat every pitch we tried.
  3. Log the passes. We threw away "not interested" threads on day one. By Thursday we wished we hadn't — the reasons people passed were the single most valuable signal for tuning next week's opener.

Should you run this on your desk?

If your CRM has >500 dormant contacts with prior warm history, yes. If it's cold data from a scraper, no — this agent will burn your number and your brand. We scope one of these in a week; book the audit and we'll tell you straight whether your list is reactivation-ready.

Next teardown (next week): the Vapi voice-screen agent. Latency budgets, barge-in handling, and why we rewrote four prompts seven times.