Automating outreach without becoming a spam machine
How I automated the preparation of Virya's outreach (research, hooks, drafts, follow-ups) while a person still decides what gets sent.
- Engineering
- Automation
- AI
- Outreach
I wanted to automate Virya outreach, but not in the "how many emails can we send before lunch?" sense. We write to festivals, radios, journalists, venues and people we may want to work with. Turning that into a volume game would save time and defeat the purpose.
Sending is the smallest part
Pressing send is the smallest part of the process. Before one useful message exists, somebody has to choose the right organisation, verify the contact route, find a real reason to write, check whether we've contacted them before, prepare the material and remember what happened afterwards.
That's the part I automated.
What the workflow does
The workflow selects a candidate, checks the available route, scores relevance, builds a factual hook, prepares a draft, detects duplicates, writes everything into the outreach log and posts a preview to Discord. An approved record may become a Gmail draft. Sending stays manual.
The factual hook
The factual hook matters most. A generic introduction to the band can be sent to anyone, which is exactly why nobody needs to read it. A useful message might refer to an open submission, a matching radio format, a recent article, an earlier conversation or a local event. AI can make those facts sound natural, but it can't make them up because the email looks a little empty.
States live in the CRM
Every candidate has a plain state: pending, approved, rejected, sent, replied, follow-up due or closed. Gmail stores the conversation; the CRM stores the process. It keeps the draft ID, thread ID, dates, next step and idempotency key, so I can query those instead of searching the inbox from memory.
Discord points to the queue
Discord only points us to the queue. It shows who the contact is, why the record matters and what needs a decision. Chat history is convenient, but it isn't a source of truth.
One reminder, then stop
The follow-up rule is simple: one initial message, one reminder, then stop after a reply, rejection or manual closure. The timer starts after sending, not after creating a draft. I added that after realising how easily the system could remind us to follow up on an email that had never left Gmail.
Social DMs stay manual
Social DMs are handled separately. APIs often don't allow a cold conversation to be started programmatically, and I'm not interested in designing around that restriction. When an email address exists, the system prepares a draft. When only a profile exists, it prepares the text and asks a person to send it.
The machine repeats the boring part, and a person decides whether the relationship is worth starting.