How it works

OutreachSphere

Sales CRM. Turns a cold lead list into deals: it fills in missing contacts, runs email and LinkedIn sequences, and sorts replies so only real leads reach a person.

Live · Formwork and its clientsVisit the site ↗

Fig.Cold outreach for Formwork and its clients, from a raw lead list to a deal in the CRM.

Architecture

  1. APeople· 4Campaign managers · SDR desk · Client portal · Claude
    • Campaign managersNext.js 15 · React 19UI

      UI · Next.js 15 · React 19

      Formwork’s team: lead lists, campaigns, sequences and the CRM, across every client, each client’s data kept apart.

      Talks to

      • → Lead lists (CSV)
    • SDR deskcalls · follow-upsUI

      UI · calls · follow-ups

      Where reps work the follow-up queue, approve drafted replies and log calls.

      Talks to

      • → Reply approvals (approve)
    • Client portalper clientUI

      UI · per client

      Each client sees their own campaigns, leads, pipeline and campaign health.

      Talks to

      • → CRM (their pipeline)
    • ClaudeMCP clientEXTERNAL

      EXTERNAL · MCP client

      Claude, connected with a personal token, working the same data through MCP.

      Talks to

      • → MCP server (MCP · token)
  2. BApp· 10Lead lists · Enrichment · Sequences · Webhook intake · MCP server · CRM · Opportunities · Reply approvals · Reply triage · Research & prep
    • Lead listsCSV · domain dedupeSERVICE

      SERVICE · CSV · domain dedupe

      Uploaded lists, cleaned and matched to companies by web domain. Uploading never marks anyone as contacted.

      Talks to

      • ← Campaign managers (CSV)
      • → Enrichment (gaps)
      • → Sequences (filtered)
    • Enrichmentwaterfall per fieldSERVICE

      SERVICE · waterfall per field

      Fills each missing field (email, phone, title, location) by trying providers in priority order, with rate limits and caching.

      Talks to

      • ← Lead lists (gaps)
      • ← Enrichment drain (queued)
      • → Data providers (in turn)
    • Sequencesemail · LinkedInSERVICE

      SERVICE · email · LinkedIn

      A sequence filters a live list into the leads it will contact, then hands them to the sender for its channel.

      Talks to

      • ← Lead lists (filtered)
      • → Smartlead (push)
      • → Valley
    • Webhook intakeevents · repliesSERVICE

      SERVICE · events · replies

      Receives the senders’ events. Only these can mark a contact as reached, which keeps the numbers honest.

      Talks to

      • ← Smartlead (webhooks)
      • ← Valley (webhooks)
      • ← Nylas (webhooks)
      • → Reply triage (replies)
    • MCP server100 tools by roleAI

      AI · 100 tools by role

      Tools grouped by area and composed per role: an admin sees all of them, other roles only their own. Tokens are stored hashed.

      Talks to

      • ← Claude (MCP · token)
    • CRMcompanies · contactsSERVICE

      SERVICE · companies · contacts

      Warm contacts and their companies, with status, temperature, notes and a relationship score from 0 to 100.

      Talks to

      • ← Reply approvals (promote)
      • → Opportunities (deal)
      • ← Client portal (their pipeline)
      • → PostgreSQL
    • Opportunitiesproposal → won / lostSERVICE

      SERVICE · proposal → won / lost

      Deals, from proposal to won or lost.

      Talks to

      • ← CRM (deal)
    • Reply approvalsdrafted answersSERVICE

      SERVICE · drafted answers

      Real leads queue here with a reply drafted. A person approves it before it goes.

      Talks to

      • ← Reply triage (real leads)
      • ← Reply pre-drafts (drafts)
      • ← SDR desk (approve)
      • → Smartlead (reply)
      • → CRM (promote)
    • Reply triagerules → ClaudeAI

      AI · rules → Claude

      Cheap rules catch bounces, unsubscribes and warmup mail first. Claude sorts the rest into six kinds, and says unsure rather than guess.

      Talks to

      • ← Webhook intake (replies)
      • → Claude · OpenAI (classify)
      • → Slack (alert)
      • → Reply approvals (real leads)
    • Research & prepClaude · FirecrawlAI

      AI · Claude · Firecrawl

      Researches a contact or their company and preps a sales meeting, and answers questions about the CRM.

      Talks to

      • → Vector store
  3. CJobs· 3Enrichment drain · Reply pre-drafts · Sender sync
    • Enrichment draincronJOB

      JOB · cron

      Works through queued enrichment in the background.

      Talks to

      • → Enrichment (queued)
    • Reply pre-draftscronJOB

      JOB · cron

      Drafts answers ahead of time for confident real leads, so the queue is ready when someone opens it.

      Talks to

      • → Reply approvals (drafts)
    • Sender synccronJOB

      JOB · cron

      Pulls campaign stats from Smartlead and sends replies scheduled for later.

      Talks to

      • → Smartlead
  4. DData· 2PostgreSQL · Vector store
    • PostgreSQLPrismaSTORE

      STORE · Prisma

      Every client’s leads, lists, campaigns and CRM, scoped by client.

      Talks to

      • ← CRM
    • Vector storePostgres · embeddingsSTORE

      STORE · Postgres · embeddings

      A separate database of contact embeddings for semantic search in the assistant.

      Talks to

      • ← Research & prep
  5. EOutside· 6Data providers · Smartlead · Valley · Nylas · Claude · OpenAI · Slack
    • Data providersProxycurl · Findymail …EXTERNAL

      EXTERNAL · Proxycurl · Findymail …

      Proxycurl, Findymail, Apollo and Hunter, among others, tried in turn for each missing field.

      Talks to

      • ← Enrichment (in turn)
    • SmartleademailEXTERNAL

      EXTERNAL · email

      Sends the email sequences, and sends approved replies on the same thread.

      Talks to

      • ← Sequences (push)
      • → Webhook intake (webhooks)
      • ← Reply approvals (reply)
      • ← Sender sync
    • ValleyLinkedInEXTERNAL

      EXTERNAL · LinkedIn

      Runs the LinkedIn sequences.

      Talks to

      • ← Sequences
      • → Webhook intake (webhooks)
    • NylasmailboxesEXTERNAL

      EXTERNAL · mailboxes

      Connected mailboxes, for replies that arrive outside a sequence.

      Talks to

      • → Webhook intake (webhooks)
    • Claude · OpenAImodelsEXTERNAL

      EXTERNAL · models

      Claude for triage, drafting and research; OpenAI for embeddings.

      Talks to

      • ← Reply triage (classify)
    • Slacktriage alertsEXTERNAL

      EXTERNAL · triage alerts

      Triage posts real leads to the team’s channels.

      Talks to

      • ← Reply triage (alert)
Connections · 24

→ request · ⇢ webhook, queue or schedule

  1. Campaign managers → Lead lists · CSV
  2. Lead lists → Enrichment · gaps
  3. Enrichment drain ⇢ Enrichment · queued
  4. Enrichment → Data providers · in turn
  5. Lead lists → Sequences · filtered
  6. Sequences → Smartlead · push
  7. Sequences → Valley
  8. Smartlead ⇢ Webhook intake · webhooks
  9. Valley ⇢ Webhook intake · webhooks
  10. Nylas ⇢ Webhook intake · webhooks
  11. Webhook intake → Reply triage · replies
  12. Reply triage → Claude · OpenAI · classify
  13. Reply triage ⇢ Slack · alert
  14. Reply triage → Reply approvals · real leads
  15. Reply pre-drafts ⇢ Reply approvals · drafts
  16. SDR desk → Reply approvals · approve
  17. Reply approvals → Smartlead · reply
  18. Reply approvals → CRM · promote
  19. CRM → Opportunities · deal
  20. Client portal → CRM · their pipeline
  21. Claude → MCP server · MCP · token
  22. Sender sync ⇢ Smartlead
  23. Research & prep → Vector store
  24. CRM → PostgreSQL

Inside: reply triage

Most replies to cold email are noise. Triage stores every reply first, lets cheap rules catch the obvious, has Claude sort the rest, and only ever lets a person send, with the CRM updated before the one step that can’t be undone.

  1. AArrive· 3Smartlead · Nylas · Webhook event
    • Smartleadreply webhooksEXTERNAL

      EXTERNAL · reply webhooks

      Replies to the email sequences, including ones from a colleague of the person emailed.

      Talks to

      • → Webhook event
    • Nylasmailbox webhooksEXTERNAL

      EXTERNAL · mailbox webhooks

      Replies that land in connected mailboxes.

      Talks to

      • → Webhook event
    • Webhook eventstored firstSTORE

      STORE · stored first

      Every reply is saved before it is classified, so nothing is lost and anything can be reclassified later.

      Talks to

      • ← Smartlead
      • ← Nylas
      • → Field extraction
  2. BSort· 5Field extraction · Rules · Classifier · Confidence floor · Kill switch
    • Field extractionevery known aliasSERVICE

      SERVICE · every known alias

      Smartlead has used several names for each field over time; extraction tries all of them.

      Talks to

      • ← Webhook event
      • → Rules
    • Ruleswarmup · bounces · vendorsSERVICE

      SERVICE · warmup · bounces · vendors

      Catch warmup mail, auto-replies, unsubscribes and bounces without a model call. Vendor domains an admin pins are a hard rule.

      Talks to

      • ← Field extraction
      • → Classifier (undecided)
    • ClassifierClaude Haiku · six kindsAI

      AI · Claude Haiku · six kinds

      Sorts what the rules can’t decide into a real lead, unsure, vendor mail, automated, spam or warmup noise, with a confidence and a one-line reason. A bid invitation from a platform counts as real money.

      Talks to

      • ← Rules (undecided)
      • → Confidence floor
    • Confidence floorlow → unsureSERVICE

      SERVICE · low → unsure

      A low-confidence answer becomes unsure, so a person sees it.

      Talks to

      • ← Classifier
      • → Hot channel (lead · unsure)
      • → Vendor channel (vendor)
      • → Firehose
      • → Review queue (real leads)
    • Kill switcheverything to peopleSERVICE

      SERVICE · everything to people

      One setting sends every reply to the hot channel for a person to read.

      Talks to

      • → Hot channel (override)
  3. CRoute· 4Hot channel · Vendor channel · Firehose · Review queue
    • Hot channelreal leads · unsureEXTERNAL

      EXTERNAL · real leads · unsure

      Slack, for replies a person should see now.

      Talks to

      • ← Confidence floor (lead · unsure)
      • ← Kill switch (override)
    • Vendor channelSlackEXTERNAL

      EXTERNAL · Slack

      Vendor mail, kept apart.

      Talks to

      • ← Confidence floor (vendor)
    • Firehoseevery replyEXTERNAL

      EXTERNAL · every reply

      Every classification, whatever the category, as an audit trail.

      Talks to

      • ← Confidence floor
    • Review queueone per replySERVICE

      SERVICE · one per reply

      Real leads queue for a drafted answer; creating the entry twice for the same reply is impossible.

      Talks to

      • ← Confidence floor (real leads)
      • → Draft + suggestions
  4. DAnswer· 5Pre-drafts · Draft + suggestions · Approve · Thread resolver · Send or schedule
    • Pre-draftscronJOB

      JOB · cron

      Drafts ahead of time for confident real leads; everything else drafts when someone opens it.

      Talks to

      • → Draft + suggestions
    • Draft + suggestionsreply · tags · temperatureAI

      AI · reply · tags · temperature

      One call writes the reply and suggests tags (only from the client’s existing ones, never invented), a CRM status and a temperature from cold to burning.

      Talks to

      • ← Review queue
      • ← Pre-drafts
      • → Approve
    • ApproveCRM first, send lastUI

      UI · CRM first, send last

      The reversible work (status, temperature, tags) is applied first; the send, which can’t be undone, comes last.

      Talks to

      • ← Draft + suggestions
      • → Thread resolver
    • Thread resolvercolleague repliesSERVICE

      SERVICE · colleague replies

      Finds the right thread even when the person replying was never in the campaign, using the id the original webhook carried.

      Talks to

      • ← Approve
      • → Send or schedule
    • Send or scheduleSmartlead inboxSERVICE

      SERVICE · Smartlead inbox

      Sends now or at a set time on the same thread, logs it, and marks the contact as replied.

      Talks to

      • ← Thread resolver
Connections · 16

→ request · ⇢ webhook, queue or schedule

  1. Smartlead ⇢ Webhook event
  2. Nylas ⇢ Webhook event
  3. Webhook event → Field extraction
  4. Field extraction → Rules
  5. Rules → Classifier · undecided
  6. Classifier → Confidence floor
  7. Confidence floor → Hot channel · lead · unsure
  8. Confidence floor → Vendor channel · vendor
  9. Confidence floor ⇢ Firehose
  10. Kill switch → Hot channel · override
  11. Confidence floor → Review queue · real leads
  12. Review queue → Draft + suggestions
  13. Pre-drafts ⇢ Draft + suggestions
  14. Draft + suggestions → Approve
  15. Approve → Thread resolver
  16. Thread resolver → Send or schedule

Two flows

A lead list becomes a pipeline

Walk through it and each step lights the parts it runs through in the drawings above.

By count

channels: email and LinkedIn
2
kinds of reply, sorted
6
actions Claude can take in it
100

Notes

  1. N.1Nobody counts as contacted until the email has actually gone out, so the numbers a client sees are real.
  2. N.2An invitation to bid sent by a bid platform is automated, but it’s still real money. The sorter is told that, and when it isn’t sure it says so and a person looks.
  3. N.3Clients get their own portal with their campaigns, leads and pipeline.

Built by

I design, architect and build software products like this one.

naram@hey.com·See the rest of my work

More systems