Open your workspace
Playbook · October 2026
Autopilot on since July 2026

The self-learning outbound engine. How I built it, end to end.

An agent that finds the right people, writes to each of them with a real reason, sends at a human pace, handles the replies and gets measurably better every week. This is the architecture, the guardrails, the order to build it in, and the lessons that cost real time. It runs Re:Vault's own pipeline and our clients' campaigns today.

smart_toyAgents
1
one brain, all the tools
scheduleScheduled jobs
102
systemd timers on one VM
verified_userSelf-checks
281
run every hour
scienceTests
292
one per rule in code
deployed_code00 · WHAT YOU'RE BUILDING

A small distributed system with a model inside

An outbound agent is a set of scheduled loops that look for work, a knowledge base that tells them what is true, a database that holds every lead and every touch, and exactly one door through which anything reaches the outside world. The model is the easy part. The plumbing that makes it safe to leave running overnight is where the weeks go.

Almost all the engineering sits at the edge. A wrong draft costs nothing. A wrong send costs a relationship, and on LinkedIn it can cost the profile. Build from the edge inwards and the rest follows.

radarSignalfunding, hiring in sales, a post, a job change
person_searchSourcethe person who owns the problem
edit_noteWriteone checked fact, in the sender's voice
sendSendthrough one governed door
insightsLearnreply rate by signal, segment, voice
architectureTHE ONE-SENTENCE ARCHITECTURE

A Git repo is the source of truth for how the agent behaves, Postgres is the source of truth for every lead and every touch, a single governor process is the only thing allowed to send, and the human holds the gates where a mistake can't be undone.

What you need before day one

  • A Linux VM you control with root. It has to be awake at 3am.
  • A private Git repo for rules, skills and knowledge.
  • Postgres with tables for leads, batches, outbox, messages, events and decisions.
  • API access to the channels: Unipile for LinkedIn, IMAP and SMTP for email.
  • Email domains that are separate from your main one and already warm. Start this first, it has the longest lead time.
  • A chat channel for the human (we use Telegram) and a kill switch you wrote before you needed it.
smart_toy01 · ONE AGENT

Split by authority, keep one brain

I started with three named agents: an orchestrator, a lead hunter and an inbox handler. Then a fourth crept in: the persona that talked to me in chat. It was a plain model call with snapshots of a few files and zero tools. Asked "did the message go out?", it could only promise to check, and it handed the real work to a separate process. In one ten-minute stretch the chat gave me three different answers to the same question.

Today there is one agent. Every turn of the conversation is a full Claude Code session inside the project folder with the database, the mail, the CRM and the skills at hand. It looks, then answers with the fact. A typical turn takes 6 to 20 seconds.

admin_panel_settings
The owner
Full session with every tool, identified by Telegram account id, not by what the message claims.
groups
Clients and partners
A model call with no tools at all, plus live campaign numbers that code puts into the context: sent today, accepted, replied, next touches.
person
Strangers
A sales mode with one public file in context: the price ladder, the booking page, and a hand-off to the human for anything beyond it.
keyRULE WORTH ADOPTING ON DAY ONE

The boundary lives in the device, never in the prompt. A stranger gets a call that physically holds no tools, so no wording of a message can reach a shell.

menu_book02 · THE CANON

Every rule carries its reason and its lock

The agent reads one canon file before work: the offer, the price ladder, the voice rules, the gates. Each rule is written in the same shape: what we do, the owner's words with a date, why, and the name of the code that enforces it. A rule without a lock is a wish, so most rules point to a database trigger, a lint in the send path, or a self-check.

FileWhat it holdsWho changes it
canonOffer, pricing, voice, gates, the list of rules and their locksThe owner's word, recorded with a quote
icp-currentSegments, signals, message hypotheses, live A/B tests, exclusionsThe weekly learning run, one change per week
icp-logEvery iteration with its numbersAppend only
playbookAccumulated lessons, each with its sourceAny run that learned something
decision logEvery change with why, expected result and the rejected optionsCode refuses a change without all three

One fact lives in one home. A client's live status sits in that client's state file and nowhere else; summaries are rebuilt from it. When two files disagree, the status check refuses to answer and says the data needs reconciling, which beats a confident wrong status.

record_voice_overOUR PROPOSAL IS NOT THE OWNER'S DECISION

A decision gets recorded as the owner's only with a quote from a channel, with date and time. The agent once wrote "decided to hand over his own profile" into a client card when no such conversation existed. An invented decision closes a question that is still open, so it does more damage than a missing one.

door_open03 · ONE DOOR OUT

Loops write to a queue, the governor sends

The hunting loop and the reply loop write rows into an outbox table and stop there. A single governor process on the VM is the only code that talks to LinkedIn. It owns the limits, the working hours, the jitter, the warm-up ramp for new senders and the gate on a sender's first message. Every rule about sending lives in one file, so it can be read in one sitting.

timer
Spread across the day
The interval equals the time left in the window divided by the touches left in the plan, recalculated every tick, at most one cold touch per profile per minute. Replies in a live conversation go out right away.
menu_book
Limits come from the vendor's docs
Before setting a cap, read what the API provider and the established tools publish, then compare with your own measurements. A rule we once made up by common sense cost a client weeks of silence; ten minutes in the provider's docs had the number.
mail
Invitation notes only from premium profiles
On our own data invitations with a note were refused 66% of the time, without a note 0.3%. On basic profiles the door strips the note and the personal part moves into the first message after acceptance.
replay
A refusal costs one attempt
A platform refusal rests that profile for 30 to 60 minutes and the queue moves to another person. Three refusals in a row with nothing sent means a wall: back off for the day.
shield_person
Real people's profiles carry no experiments
Mechanics (notes, likes before inviting, volume, new action types) are tested on spare profiles. On a real person's profile only the words change. A database trigger enforces it.

Email follows the same shape: messages go into an email outbox, a separate governor sends them from pre-warmed domains kept apart from the company domain, at 20 to 30 a day per mailbox, spread through the day. Three SMTP failures in a row count as a breakage.

database04 · SOURCING AND DATA

Pay at the moment of the touch, for one person, after scoring

Over five days in early October we bought about 5,000 enrichment credits for 614 new people: eight per person where one was enough. Company cards were bought before scoring, the hunt ran every three hours whatever the queue held, and one paid step was configured at a price of zero, so no cap could see it. Here is the rule that replaced it.

calculate
Need comes from tasks
Daily limit times five days, minus what's queued, minus usable leads already in the database. Zero means no hunt. The estimate is written before anything is bought.
filter_alt
Zero-cost steps first, all the way down
Our own database, then people search at zero cost that already returns industry, size and country, then the paid steps in a fixed waterfall behind one door. Callers never pick a vendor.
rule
Score first, pay after
Each candidate is scored 0 to 4 on the fields we already have. A person is unlocked when they enter the queue; a company card only when those fields are empty. The norm is one credit per queued person.
lock
Three locks outside the code
A spend cap on the key at the vendor, a daily budget per client that pauses the hunt, and a live balance counter checked every hour. Anything bought is reused for 14 days.
edit_note05 · WHAT A MESSAGE READS

One checked fact, one voice, one door that cuts it

A first message carries at least one fact about the person that can be checked, and the fact carries its source: the brief, the data provider, the profile, a web page. A lead without a fact is dropped. When the signal is a job post, it counts only if they are hiring in sales and growth, the company sells to businesses, and the record holds where and when the post was seen, word for word.

Voices are tested, and the database stamps them

First touches go out in several voices at once: the founder's voice with a question for permission, a plain statement of what we do, a hook question, and a micro-demo that contains a ready message for the recipient's own client. The voice comes from the lead's id, so it is fixed for the whole thread, and a trigger on the outbox stamps the voice on every row, follow-ups included. The weekly report reads that stamp and compares voices once each one has 30 texts and 3 replies.

A stamp is a frequency, not a word

We banned "curious" and got "worth 15 minutes" 58 times in the next batch. Banning a phrase moves the machine smell into the next phrase. So the check counts: no exact repeats across one client's batch, a single five-word sequence in at most 30% of texts, a single closing line in at most 25%. The hunt runs it before a batch is queued.

format_align_left
Paragraphs are cut by the door
From three sentences up, the governor splits a message into short paragraphs before sending. The words stay; the wall of text disappears.
horizontal_rule
No long dashes, anywhere
A database CHECK constraint on the outbox rejects any text containing one. Commas, colons and full stops do the job.
near_me
Selling yourself: get to the point
The first touch names what we sell, stays under 400 characters, and by the third message there is an offer to talk. An objection is answered with a proposal.
forum06 · REPLIES

A ready answer and 48 hours for a word

A webhook drops every reply into Postgres, and a dispatcher wakes the reply loop only for new work. The loop classifies the reply. Safe classes (a slot, a factual question, a booking) are answered automatically. Price, pitch, negative and ambiguous replies become a draft for a human.

For clients, the draft becomes an email: "a lead replied, here is a suggested answer, approve it within two days or it goes out as written". In the dashboard the client sends, edits or holds it. Silence after 48 hours sends it.

replay
The first no gets one more step
A first refusal gets one sentence that takes the reason seriously and offers one light next step. "Wrong person" gets a question about who owns it. A second no, or a request to stop, closes the thread.
hearing
The answer covers everything they sent
Every incoming item since our last message is numbered, and the send door requires a note on each one. A judge model then compares the draft with the thread. It exists because a reply once ignored a person's video and their own booking link, and asked what the video had already answered.
event_repeat
Silence gets a short question later
After two unanswered touches the thread stays open. Ten days later a short question about timing; forty-five days later one more. Before each, the loop looks at the profile and names a reason only if it sees one.
insights07 · THE LEARNING LOOP

Tag everything, change one thing a week

Every batch carries one signal, one segment and a date. Without the tag the analytics are dead on arrival. Attribution recalculates every 15 minutes. Once a week the learning run computes reply rate by signal, segment, message hypothesis and voice, and applies at most one change to the ICP, only on data with at least 30 sends and 3 replies.

# every change is a row with three required fields why: the numbers that triggered it, by signal and segment expected: the result we predict, with a number and a date rejected: the other candidates, each with its reason code # the loader refuses a decision with any field empty

A forecast also needs an exit. A weekly check compares each decision's expected result with the facts and closes it. A prediction left without an outcome turns red, and so does an experiment without a verdict after 45 days.

tuneWAIT FOR REAL REPLIES

Expect the first fifty real replies to change your mind about several things. Tune on replies, and keep warm-up traffic and test sends out of the corpus.

verified_user08 · EVERY FEATURE HAS A CHECK

A feature counts as done when a machine can say it's broken

Every new capability brings a line in the self-check: a question the machine can answer with "broken" without a human. The self-check runs every hour, and anything red reaches the owner once a day per code. A rule held by code (an index, a trigger, a retry) beats a rule held by text.

category
Each check has a class
Breakage wakes the human. Debt wakes the loop that owns it and becomes a breakage after five days. External wakes nobody.
hourglass_bottom
Each check has a way to go quiet
The criterion of the check equals the criterion of the fix. A check that can never go green is a siren, and people stop listening to sirens. New checks sit in quarantine for three days.
person_check
Measure what the person received
Ask the question after the last link between you and the recipient. Our sameness check once read invitation notes and reported 2% repeats for a client whose sent emails had 48%. A check on the wrong corpus fails more quietly than a missing one, because people trust green.
notifications_off
Absence must alarm
The evening report is written to a file; if it's missing by 23:00, a check turns red. A check that only reacts to the data it receives goes quiet exactly when the thing it watches dies.
notifications09 · THE HUMAN'S CHANNEL

Only what broke and what waits on you

Every message to the owner names its class: alert, needs-you, event, brief. Everything else is routine and stays in the logs. A message that says "nothing needed from you" never gets sent. A second filter asks when: only what needs action today or tomorrow goes out on its own, and the rest becomes a line in the evening digest.

hourglass_empty
Nothing stops while waiting for an answer
Every wait has a safe default, and silence means the default. A question is allowed only where no default exists: money, contract, price, a first send from someone else's name.
local_gas_station
Preparation is never gated
Sourcing, enrichment and queuing run without approval. Every active sender keeps at least three working days of queue; a fuel check tops it up to five and orders a hunt by itself.
wb_twilight
Sweeps instead of pings
Every six hours a sweep goes through mail, messengers and client chats. Each request older than six hours gets one outcome: closed by the agent, one line to the owner, or after twelve hours a personal note to the person waiting that it's being worked on.
power_settings_newKILL SWITCH

One file on the VM or one phrase in chat stops every autonomous run. Write it before you need it.

stairs10 · THE ORDER TO BUILD

Each rung has a gate you pass when it's clean

0
The box and the kill switch
VM, secrets outside Git, spend cap on the model provider, backups proven by a restore. Start warming email domains today.
Gate: one command stops everything
1
The canon
Offer, ICP, voice, the claims you can make with their sources.
Gate: you approved every claim yourself
2
The database and the one door
Leads, outbox, events, decisions. Loops write to the outbox; the governor is the only sender.
Gate: nothing else in the codebase can send
3
The guardrails in the send path
Suppression list, dedup against the live platform, one fact with a source, style limits.
Gate: a set of known-bad drafts, all blocked
4
One full loop by hand
Research one account, draft, watch the guardrails block a deliberately bad draft, approve the good one.
Gate: nothing left the building unapproved
5
Replies first, then outreach
Inbound is the speed-critical path. Automate classification and drafts, then safe classes, then first touches on a spare profile.
Gate: two weeks with zero wrong sends
6
The learning loop and autopilot
Tags on every batch, the weekly run, one change at a time. Then hand over the gates that have a safe default.
Gate: 30 sends and 3 replies before any change
school11 · LESSONS

Things that cost us real time

The specifics are the useful part. Each one changed how the system is built.

alternate_email
The general mailbox that looked like a door
A contact told a client to write to hr@ and named no person. We read it as a route; five days later an auto-reply arrived with a ticket number. The actual owners took one minute to find.
What we changedA role mailbox never counts as a contact. A company is open when a person is named; the agent finds names, work emails and phones itself and hands the client an ordered list.
block
The stop list that never reached the sender
A client's existing customer sat on our exclusion list and was correctly skipped on LinkedIn. The email provider built its own audience, its suppression list was empty, and the customer received a cold email.
What we changedEvery 15 minutes the exclusions are converted to domains and pushed into the provider's suppression list, checked right before each send. A name without a domain goes to the client as a question.
schedule
The 08:30 meeting in the wrong language
A prospect picked 8:30 in a thread written entirely in German. The calendars were empty, so the agent booked it. It was moved twenty minutes before the start.
What we changedNo meeting before 09:30, 15 minutes of buffer on both sides, and everyone writing in another language is told the call will be in English before it's booked.
sync_problem
Edits that quietly reverted
The project folder on the laptop was synced through a cloud drive while the VM held the real copy. Edits made on the laptop rolled back on the next sync.
What we changedThe VM is the canon, the laptop is a read mirror, and a guard file refuses to start services from the mirror.
payments
The price that lived in one email
A discount agreed with a client existed only in an email and on the client's offer page. For three mornings the task list asked the founder to name a number he had named long ago.
What we changedEvery exception to the price ladder lives in one file, as one row with the owner's quote and date.
event_busy
The date that disagreed with its weekday
A weekday next to a date that belongs to another day. On our side it's a typo; on theirs it's a lost slot, and only a calendar can catch it.
What we changedA lint in the mail door and the messenger door checks every weekday that sits next to a date.

The principles underneath

  • A rule lives where it's measured. Text in a canon is a reminder; a trigger is a rule.
  • Verify against the running system. Docs about your own system, including the ones an earlier session wrote, are claims until a live call confirms them.
  • Fail closed where safety is involved. Under-selecting costs a day; over-selecting writes to someone you promised to leave alone.
  • One brain, boundaries in the device. Personas are cheap; authority is enforced by which tools a call can physically reach.
  • Measure before you build the clever thing. The real distribution of your data is usually tamer than the edge case in your head.
boltRUN IT ON YOUR PIPELINE

This engine already works for B2B teams

Connect your own Claude or ChatGPT to the stack from $199 a month, or have us run the whole thing for $2,000 a month: we set it up for your market and look at it every day, you read one email on Friday and take the meetings. Month to month.