James StoneOPEN TO CONVERSATIONS
← All projectsGrowth / Marketing

Custom Outbound Machine

Flask app · outbound campaign ops

A local Flask app that turns a pile of timestamped campaign CSVs into an outbound operating system — a live funnel, and a mailbox reconcile that works out which drafts actually went out.

Problem

An existing Python toolchain could build cohorts, enrich provider data and push email drafts to Outlook — but nobody could see what was happening. Campaign state lived implicitly in timestamped filenames scattered across OneDrive, and because advancing a sequence step rewrites the working set, reading the latest file silently reset the sent / replied / booked counts to zero. Sending is manual by mandate, so the tracking sheet couldn't even be trusted to know which of 100+ drafts had actually gone out.

Build

Built a local Flask app — no database, no cloud, no LLM API key, deliberately — that derives campaign state by reading the files on disk, shows a live funnel and a lifecycle stepper, and reconciles the mailbox via Microsoft Graph to work out which drafts were sent, replied to, or bounced. ~2,700 lines of app code with 75 tests, wrapping ~4,850 lines of existing scripts left untouched. Writes are paranoid: every status change is backed up, atomic, and applied only if the row is still in its expected state.

Outcome

A team of two-to-four now runs campaigns from a shared folder without learning the script chain. State that used to mean opening the right CSV and counting by hand is a dashboard reporting the cumulative truth — pushed 118, sent 112, replied 7, booked 2 — instead of resetting to zero. Every send is still reviewed by a human; the tool serves the operator's judgement rather than replacing it.

Inside the repoLocal Flask app · Python · 75 tests
Cognassist-Outbound-Machine/
├── app.py             # Flask app — 14 routes + helpers            (523)

│   ── THE APP'S OWN CODE · 2,188 lines ──
├── lib/
│   ├── campaign_state.py   # Reader, Campaign/ContactRow, lifecycle (684)
│   ├── registry.py         # campaigns.json registry + create       (180)
│   ├── jobs.py             # In-memory background jobs + progress    (107)
│   ├── graph.py            # MS Graph auth + Sent Items/Inbox        (270)
│   ├── reconcile.py        # Pure sent/reply/bounce matching         (291)
│   ├── tracking_writer.py  # Safe, backed-up, atomic write-back      (131)
│   └── engine/             # Capsule cohort build + enrichment       (407)

│   ── INTERFACE ──
├── templates/         # Server-rendered Jinja2, no SPA              (677)
├── static/            # Plain CSS + ~77 lines vanilla JS            (460)

│   ── VERIFICATION ──
├── tests/             # 75 tests across 8 modules                 (1,171)

│   ── WRAPPED, NOT REWRITTEN ──
└── scripts/           # Pre-existing toolchain, reused untouched   (4,845)