Building an Internal Campaign Operations System
Turning raw CRM/ERP contact data into reusable campaign audiences — without exports, ad-hoc filtering, or chasing technical support.

CAMPAIGN PIPELINE · AUDIENCE OPS · V2.1 · INTERNAL
0103
In plain English
I helped internal teams turn CRM/ERP contact data into campaign-ready audiences they could prepare once and reuse, with clear visibility into who was being contacted, when, and what came back.
Business value
- Less manual audience preparation
- Reusable contact lists across campaigns
- Clearer campaign status visibility
- Stronger foundation for future automation
- Better alignment between sales, marketing, and operations
- Role
- Full Stack CRM/ERP Engineer
- Design · Build · Ship
- Timeframe
- March 2026 - Present
- Domain
- CRM/ERP / Marketing Operations
- Category
- Marketing Operations
- Centralised campaign prep inside the CRM/ERP — no more cross-tool exports
- Reusable marketing lists replaced one-off audience selections
- Explicit draft → scheduled → sent state machine, no boolean flag soup
- Tags + segments turned raw contacts into campaign-ready audiences
- Reports surfaced audience counts, sent activity, and engagement in-app
- Centralised campaign prep inside the CRM/ERP — no more cross-tool exports
- Reusable marketing lists replaced one-off audience selections
- Explicit draft → scheduled → sent state machine, no boolean flag soup
- Tags + segments turned raw contacts into campaign-ready audiences
- Reports surfaced audience counts, sent activity, and engagement in-app
Context
The CRM/ERP was already the central workspace for internal operations, but campaign prep had outgrown the shape it started in. Contact data lived in the CRM/ERP; audience preparation lived in spreadsheets, ad-hoc exports, and someone's memory of how last quarter's campaign was filtered.
The platform supported listings, sales, leads, and contact records used across multiple internal teams. Campaigns weren't a side feature — they were how those teams reached the right contacts at the right time. When campaign prep slowed down, everything downstream slowed with it: list quality dropped, audiences got stale, and teams started leaning on engineers for one-off filtering instead of running campaigns themselves.
The work focused on building the operational layer around campaigns — audience preparation, reusable lists, tags, segments, and state tracking — so the CRM/ERP could carry the full workflow instead of handing it off to side tools.
The Problem
The challenge wasn't sending email. The platform already had send infrastructure. The problem was everything that happens before the send: turning a pile of contacts into a clean, reviewable, reusable audience that a non-technical user could trust.
Audience prep was fragmented. Marketing teams pulled CSV exports, filtered them in spreadsheets, and re-imported the cleaned list. Tags were inconsistent — the same concept was tagged three different ways in three different campaigns. Segments existed but weren't surfaced clearly enough for marketing users to build them confidently.
Campaign state was tracked with scattered boolean flags — is_draft, is_sent, is_scheduled — which meant the UI had to guess what action to show next. Drafts that were actually mid-send got the wrong actions. Failed campaigns looked the same as completed ones until you opened the report.
Reporting visibility was thin. Teams could see whether a campaign sent, but not the audience size at send time, what came back, or which records failed. Without that loop, every campaign felt like it ran in the dark.
The goal was to fix the prep layer, not rebuild the send layer.
Ownership
Everything I designed, built, and was accountable for.
Product & UX
- Tag + segment workflows
- UX research with marketing users
Engineering
- Backend campaign + audience APIs
Operations
- Campaign reporting endpoints
Additional scope
- Campaign + audience data model
- Campaign state machine design
- Marketing list logic + reusability model
- Angular campaign + audience screens
- CSV import + validation pipeline
- Visual design language
Key decisions
The calls I made, what I rejected, and why: these are the tradeoffs that shaped the system.
Kept lists, tags, and segments as three separate concepts
Unified "audience filter" abstraction
Marketing users already had three different mental models — tags for labelling, segments for rules, lists for hand-picked groups. Collapsing them into one abstraction would have cleaned up the data model at the cost of breaking the user's intuition. Keeping them separate let each one stay legible in the UI.
System design
I built the campaign workflow around three concepts kept deliberately separate: lists (hand-picked groups), tags (loose labels), and segments (rule-based audiences). Marketing users already thought of them differently — collapsing them into one "audience filter" abstraction would have made the data model cleaner but the UX worse.
On top of that, I modelled campaign state as an explicit state machine — draft → scheduled → sending → sent → completed, with failed as a side branch from sending. Each state had its own allowed actions, its own badge, and its own report view. Replacing the boolean-flag soup with one status column collapsed a lot of conditional logic.
The audience builder pulled from any combination of lists, tags, segments, manual selections, and CSV imports — deduplicated by contact ID, with a visible count that updated as you added or removed sources. Lists were promoted to first-class reusable units: create once, reuse across campaigns, edit in place.
On the backend, campaign and audience APIs returned predictable shapes: counts at the top, contact previews paginated below, status and timestamps explicit. Angular consumed those endpoints into screens designed for marketing users — readable status badges, audience-size feedback while building, and confirmation steps on anything irreversible.
CSV import got its own validation layer: required fields checked, duplicates flagged before save, and a preview step before contacts hit the database. Reporting hooked into the send pipeline directly, capturing audience size, send activity, and engagement at the campaign level.
0104
REPORTING · ENGAGEMENT · REUSE
Frontend
Backend
Database
Also used
0103
Outcome
- Centralised audiences
- Reusable lists
- Tracked states
- In-app reporting
Campaign prep moved into the CRM/ERP. Marketing users built audiences from lists, tags, and segments without leaving the workspace, without exports, and without engineering involvement for routine sends.
Reusable lists changed how teams thought about audiences. Instead of rebuilding the same selection every campaign, lists became long-lived — edited as the audience evolved, reused across quarterly sends and ad-hoc pushes alike.
The state machine fixed the "what's actually happening to this campaign" question. Drafts looked like drafts, scheduled looked like scheduled, failed looked like failed, and the UI matched. Confidence in campaign status went up, and so did the willingness to schedule sends in advance instead of waiting to click manually.
Reporting closed the loop. Audience size at send time, completion status, engagement counts, and failed-record visibility all surfaced inside the CRM/ERP — no separate analytics tab, no chasing exports.
The bigger win wasn't any single feature. It was that campaigns stopped being a side workflow and became part of the CRM/ERP's daily operational rhythm.
Centralised campaign prep across marketing + ops teams
0104
No engineer needed for routine sends
One list, many campaigns
Single source of truth
Closed feedback loop
Marketing and operations teams could prepare and run campaigns without engineering involvement, with reusable lists that survived data churn and clear visibility into campaign state. The CRM became the operational home for campaigns instead of a contact database that fed other tools.
“Rusty understands the difference between adding features and making software actually usable. He looks at how people work, finds the friction, and improves the system in a way that makes daily operations feel smoother.”
Operations Stakeholder
Internal Platform Team — name under NDA
Reflection
I'd start with the state machine next time. I built it after lists and audiences were already in flight, which meant unwinding boolean flag logic that had spread further than I expected. Modelling campaign state as a first-class concern up front — even on day one — would have saved a meaningful amount of refactoring.
I'd also push harder on tag governance earlier. Tags are easy to add and hard to consolidate; by the time we noticed three different spellings of the same concept, real campaign data was already attached to all three. A simple admin tool for merging tags, plus light validation on tag creation, would have prevented most of the cleanup work later.
The biggest lesson is that audience preparation is the campaign system. Send infrastructure is commodity; what marketing users actually need is confidence that the audience is right, the list will still be there next quarter, and the campaign state on screen matches reality.
8+
3
5+
10+
Have a CRM/ERP campaign workflow that's outgrown spreadsheets?
I help internal teams turn raw CRM/ERP data into operational campaign workflows. Let's talk about your project.
Start a Project