An AI SDR fails at the boring part. Writing a decent cold email is easy now; the thing that breaks is two agents, or an agent and a person, disagreeing about who has already been contacted. One of our users runs outbound with two agents, Claude to plan and a Grok Bot to send, and the setup below is how they keep it from emailing the same business twice. We've changed the industry and other details to keep them anonymous, and left out the numbers. What's left is the architecture, which is the part worth copying.
What does the setup look like?
Two agents, one job each, and one shared table between them:
- Claude is the planner. It researches the target segment, builds the contact list, writes and revises the copy, and decides who goes into each round.
- A Grok Bot is the sender. Every hour, it takes the next batch of contacts that are ready, sends, and records what it sent.
- A shared contact table is the source of truth. Every contact is one row with one unique key, and every status change happens there.
- A Google Sheet mirrors the table so the pipeline is easy to scan.
Why split planning and sending between two agents?
Because they need opposite rules. A planner should be free to rethink: drop a segment, rewrite a subject line, reorder the list. A sender should do exactly what the row says and nothing else. When one agent does both, a plan change mid-round leaks into sends, and you can't tell afterwards whether a strange email was a decision or an improvisation.
It also gives the sender a job small enough to be dull: every hour, the same few steps on the next batch. Dull is what you want from the agent that touches people's inboxes.
What breaks first?
Duplicate contacts. That's the problem this user built the setup around, and it's the one every multi-agent outbound setup hits. When a list comes from several sources, the same company shows up under two names, or the same person under two addresses. Each agent dedupes its own view, and neither dedupes the other's. Two more follow close behind in setups like this:
- A batch sends twice when the sender stops mid-round and can't tell which sends finished.
- A decision gets undone. Someone is removed from the list for a good reason, and a later research pass adds them back because the reason lived in a chat the new pass never read.
None of these are AI problems. They're what happens when two people share a list without a system, just faster.
How do you stop two agents contacting the same person?
One key, one claim, one place. This user dedupes on a single key and puts a send lock on each contact; here's how we'd set that up from scratch.
- Pick one dedupe key and normalize it. We'd use the business's web domain, lowercased with
www.stripped, because a company has one domain but many names and inboxes. Make that column unique so a second row for the same company can't be created at all. - Claim before you send. The sender marks a row as taken before it sends and releases it with a result after. Another agent, or a restart of the same one, sees the claim and skips the row.
- Statuses only move forward.
ready → sending → sent → repliedorready → removed. A removed row stays in the table with its reason, so a later research pass finds it and doesn't add it back.
The Sheet mirrors those rows for reading and is never written back from. Two-way sync between a Sheet and an agent's store is the classic source of double writes: an edit on one side, then a stale overwrite from the other.
What does a handoff between the agents contain?
The reason, as well as the result. When the planner finishes a round it leaves a short note the sender reads first: which rows are ready, what changed since the last round, and why. When the sender finishes, it leaves what it sent and anything odd, like a run of bounces or a reply that needs a person. "Removed the resellers, they never buy" is the line that stops the next research pass from undoing a decision.
What would we add?
Two things, from our own outbound rather than this user's.
Put a human approval on the first send to any new segment, not just on the copy. A list can be right row by row and wrong as a whole, and that's the error an agent is slowest to notice.
Keep the sender boring. Every bit of room you give the sending agent to adapt, like retrying differently or tweaking copy for one contact, creates a case nobody decided on. A sender with no judgment and a planner with all of it is the version that holds up.
How Tokenrip runs this
The shared table, the claims and the handoff notes in this setup live in a Tokenrip workspace. Claude and the Grok Bot both connect to it over MCP, read the pinned rules and the latest handoff before they start, and work the same contact table, where the unique key and the claim on each row are what make double contact impossible rather than unlikely. Every change is kept as a version, so "who removed this contact and why" has an answer. If you run outbound with more than one agent, or with an agent and a teammate, start with the table and the key; How to manage multiple AI agents covers the general rules.
To connect your agent, paste this into it:
Connect me to Tokenrip. Fetch https://tokenrip.com/setup?ref=ai-sdr-multiple-agents and follow it.