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 ↗
Architecture
Hover a box to trace its connections; click it for what it does.
- request
- webhook, queue, schedule
- the heart
- outside service
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)
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
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
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
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
- Campaign managers → Lead lists · CSV
- Lead lists → Enrichment · gaps
- Enrichment drain ⇢ Enrichment · queued
- Enrichment → Data providers · in turn
- Lead lists → Sequences · filtered
- Sequences → Smartlead · push
- Sequences → Valley
- Smartlead ⇢ Webhook intake · webhooks
- Valley ⇢ Webhook intake · webhooks
- Nylas ⇢ Webhook intake · webhooks
- Webhook intake → Reply triage · replies
- Reply triage → Claude · OpenAI · classify
- Reply triage ⇢ Slack · alert
- Reply triage → Reply approvals · real leads
- Reply pre-drafts ⇢ Reply approvals · drafts
- SDR desk → Reply approvals · approve
- Reply approvals → Smartlead · reply
- Reply approvals → CRM · promote
- CRM → Opportunities · deal
- Client portal → CRM · their pipeline
- Claude → MCP server · MCP · token
- Sender sync ⇢ Smartlead
- Research & prep → Vector store
- 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.
Hover a box to trace its connections; click it for what it does.
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
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)
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
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
- Smartlead ⇢ Webhook event
- Nylas ⇢ Webhook event
- Webhook event → Field extraction
- Field extraction → Rules
- Rules → Classifier · undecided
- Classifier → Confidence floor
- Confidence floor → Hot channel · lead · unsure
- Confidence floor → Vendor channel · vendor
- Confidence floor ⇢ Firehose
- Kill switch → Hot channel · override
- Confidence floor → Review queue · real leads
- Review queue → Draft + suggestions
- Pre-drafts ⇢ Draft + suggestions
- Draft + suggestions → Approve
- Approve → Thread resolver
- 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
- N.1Nobody counts as contacted until the email has actually gone out, so the numbers a client sees are real.
- 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.
- N.3Clients get their own portal with their campaigns, leads and pipeline.
Built by
I design, architect and build software products like this one.
More systems