Raphael Napoleon Atikpo
Project 3 of 10 — Marketing Operations & Automation portfolio

A lifecycle model that knows the difference between a lead who converted and a lead who was never allowed to.

A lifecycle-stage and attribution architecture for Anchorpoint TMS, a fictional B2B fleet-management SaaS company — five controlled stages instead of HubSpot's full default set, two custom properties instead of a sprawling data model, and a qualification gate that correctly blocks even a high-intent form submission when fleet size or consent doesn't clear the bar. Built entirely on free-tier HubSpot, proven against five real test contacts pushed through the actual funnel mechanics.

Demo Request Form→ Lifecycle: Lead→ MQL Gate (Fleet + Consent)→ SQL Call→ Deal Created → Opportunity→ Deal Won → Customer
The actual pipeline this page describes — every arrow below is backed by a real screenshot from five test contacts pushed through the full funnel, not a diagram of an intention.
2
new custom properties built
5
lifecycle stages actively used
6
test scenarios proven
$0
total budget

The problem

Anchorpoint Software, Inc. sells Anchorpoint TMS — a transportation management platform for regional trucking and logistics carriers — through a content-led, sales-assisted motion: demo requests and gated guides from marketing, a human SDR qualification call, then an Account Executive. Marketing and sales disagreed on exactly one recurring thing: what a contact's Lifecycle Stage actually meant at any given moment, and whether someone who filled out the demo form twice but never answered a call should read the same as someone who'd had an actual sales conversation. As the company's second Marketing Operations hire — building on an existing tracking system and consent architecture from two prior projects — the brief was to make Lifecycle Stage and attribution data trustworthy enough that scoring and ROI work could be built on top of it later without re-litigating what each stage meant.

What I set out to build

How the pieces fit together

The system runs as two layers that only meet at two points: when a contact is created, and when a deal changes state.

Track A — Native & automated

HubSpot's own Lifecycle Stage automation (Settings → Objects → Contacts → Automate tab) drives every deal-triggered transition — SQL→Opportunity on deal creation, Opportunity→Customer on Closed Won — backed by a native guardrail that refuses to move a record backward.

Track B — List-reviewed & custom

Two purpose-built properties (Qualification Reason, Fleet Size) plus two Active Lists stand in for the Workflow engine the free plan doesn't include, carrying the one transition — Lead→MQL — that genuinely needs marketing judgment rather than a system rule.

Five controlled stages, each with its own entry condition:

Stage 1
Lead

Any identified contact, no qualification yet — the default for every new form submission or manually created record.

Stage 2
MQL

Meets ICP (20–500 trucks) and either requested a demo directly or crossed an engagement threshold — reviewed via two Active Lists, not a workflow.

Stage 3
SQL

An SDR has confirmed real fit on a call — the one transition this system deliberately never automates.

Stage 4
Opportunity

A deal exists — set automatically the instant one is created, even for a contact who skipped MQL/SQL entirely via a warm referral.

Stage 5
Customer

The deal was won — set automatically, and permanent: a later lost deal on the same contact can never undo it.

Stack

HubSpot CRM (Free) HubSpot Forms HubSpot Deals Native Lifecycle Automation Active Lists

What's actually running

Data model — two properties, not twenty

Of the full candidate list a rigorous lifecycle-and-attribution model could need, only two didn't already exist natively: Qualification Reason, a controlled dropdown recording why a gate passed or failed, and Fleet Size, a Company-level number driving the ICP check. Everything else — the five Became-a-[Stage]-Date fields, Original/Latest Traffic Source, First/Recent Conversion — is a free, default HubSpot property that simply had to be found rather than built.

HubSpot Properties settings showing Fleet Size (Number) and Qualification Reason (Dropdown select) as the only two custom properties this system required
The only two custom properties this system needed — Fleet Size (Number) on Company, Qualification Reason (Dropdown select) on Contact — confirmed against the free-tier property list.

Native lifecycle automation, not a Workflow

With no Workflow engine on the free plan, SQL→Opportunity and Opportunity→Customer run on a single native setting most free accounts never discover: Settings → Objects → Contacts → Lifecycle Stage → Automate tab. It fires the instant a deal is created or won, and comes with a guardrail that never moves a record backward — Customer stays Customer even if a later deal on that same contact is lost.

HubSpot Lifecycle Stage Automate tab showing three toggles: set stage to Lead on creation, Opportunity on deal created, Customer on deal won
The three toggles doing the work of a Workflow: default stage on creation (Lead), stage on deal creation (Opportunity), stage on deal won (Customer).

The one gate that stays manual — and the lists standing in for a Workflow

Lead→MQL is the one transition deliberately left unautomated: it requires weighing fleet size and marketing consent together, which is exactly the kind of judgment a free account can't hand to a system rule. Two Active Lists carry that judgment instead — a same-day Fast Path for direct demo requests, and a weekly Slow Path for accumulated engagement — each gated on Fleet Size (20–500 trucks) and the account's actual Marketing Consent property.

HubSpot active list filter builder for MQL Fast Path: Lifecycle stage Lead, Recent conversion contains Request a Demo, Fleet Size between 20 and 500, Marketing Consent known, Suppression Reason unknown
Fast Path: direct demo request, right fleet size, consent confirmed.
HubSpot active list filter builder for MQL Slow Path: Lifecycle stage Lead, Number of form submissions greater than 3, Fleet Size between 20 and 500, Marketing Consent known, Suppression Reason unknown
Slow Path: 3+ form submissions standing in for direct intent, same fleet-size and consent gate.

A second list for a problem that would otherwise hide

A manually created contact — a warm referral, say — never has a tracked session, so Original Source stays blank forever unless someone actually checks for it. A third list flags exactly that: anyone at MQL or beyond with no Original Source on record, which is a data-quality problem worth surfacing, not a reporting quirk to ignore.

HubSpot active list filter builder for Attribution QA: Lifecycle stage is any of MQL, Opportunity, SQL, or Customer, and Original Traffic Source is unknown
Lifecycle Stage at MQL or beyond with Original Source unknown — the system's own check on its own attribution data.

Proving it, not asserting it

Five test contacts, pushed through real HubSpot mechanics — not five contacts with properties typed in by hand — covering the positive path, two negative paths, the regression guardrail, the one accepted exception to the whole model, and attribution permanence.

1 — Full positive journey

Marcus Whitfield submitted the real demo-request form, cleared the MQL gate, passed his SQL call, had a deal created and won for him — and his Lifecycle Stage advanced automatically at every deal-driven step.

Marcus Whitfield contact record showing Lifecycle Stage Customer, Lead Status Connected, associated deal Whitfield Regional Freight closed won at $45,000
Marcus Whitfield at the end of the funnel — Lifecycle Stage Customer, Lead Status Connected, associated deal Closed Won.
Deal stage property history for Whitfield Regional Freight - Anchorpoint TMS showing a change to Appointment Scheduled followed by a change to Closed Won, both via CRM UI
The deal's own history: Appointment Scheduled → Closed Won, both logged by the CRM UI, both driving the contact's Lifecycle Stage automatically.

2 — Negative scenario: fails the SQL call

Priya Anand qualified for MQL on fleet size and a direct demo request, then failed the SQL call. Lifecycle Stage stayed at Marketing Qualified Lead — it never advanced to SQL — while Lead Status flipped to Unqualified and Qualification Reason recorded the specific disqualification.

Priya Anand contact record showing Lifecycle Stage Marketing Qualified Lead and Lead Status Unqualified, with no associated deals
Priya Anand after a failed SQL call — the system records why the gate failed without ever pretending she reached a stage she didn't earn.

3 — Accepted exception: stage-skip via direct deal

Robert Tanaka was created manually and never ran through MQL or SQL. The moment an AE created a deal for him directly, Lifecycle Stage jumped straight to Opportunity — the one path this architecture deliberately allows to bypass the funnel, since a deal is itself proof of a real sales pursuit.

Robert Tanaka contact record showing Lifecycle Stage Opportunity and Lead Status New, with associated deal Tanaka Logistics Group at $60,000, stage Appointment Scheduled
Robert Tanaka: Lead → Opportunity in one step, with Lead Status still showing New — he was never worked as an MQL or SQL.
Deal record for Tanaka Logistics Group - Anchorpoint TMS showing stage Appointment Scheduled and amount $60,000
The deal that triggered it — created directly, no prior MQL or SQL review in its activity history.

4 — Regression guardrail: import attempt

A CSV import tried to set Marcus Whitfield's Lifecycle Stage back to an earlier value after he'd already reached Customer.

HubSpot import summary for Marcus - Sheet1.csv showing Import completed with no errors
The import itself completed cleanly — no errors, no blocked rows.
HubSpot import contacts list showing Marcus with Lifecycle Stage still reading Customer after the import
And yet Marcus still shows Customer in the import's own contact list — the native forward-only guardrail held, exactly as predicted.

5 — Regression guardrail: lost deal on an existing customer

A second deal was created on the same already-Customer contact and marked Closed Lost. Lifecycle Stage never moved — losing a new sale to an existing customer isn't the same thing as them never having bought in the first place.

Deal record for Whitfield Regional Freight - Expansion showing stage Closed Lost
The lost expansion deal — and Marcus's Lifecycle Stage (shown above, Test 1) stayed at Customer regardless.

6 — Attribution permanence

Original Traffic Source and Latest Traffic Source both populated and identical for a contact whose only visit was his original form submission — exactly the behavior the permanence rule predicts, verifiable down to the actual page URL in each Drill-Down field.

Marcus Whitfield's full property list showing Latest Traffic Source and Original Traffic Source both reading Direct traffic, with matching Drill-Down URLs pointing to the demo request page
Original and Latest Traffic Source, both "Direct traffic," both pointing to the same demo-request page — permanence holding exactly because nothing has overwritten it yet.
On business impact: this is demonstrated capability, not measured business impact — there's no real company or customer base behind Anchorpoint Software. What's genuinely demonstrated is a lifecycle and attribution layer that, pointed at a real contact database, would keep Lifecycle Stage trustworthy enough for a scoring or ROI model to be built directly on top of it, without that next project having to re-verify what each stage actually means.

What I'd tell a hiring manager directly

Challenges

The most instructive snag wasn't lifecycle logic at all — it was two inverted filter operators ("is none of" instead of "is any of," "is unknown" instead of "is known") that kept three Active Lists silently empty for a full session before a direct, property-by-property comparison against a real test contact surfaced them. The second was discovering that HubSpot's "Recent Conversion" property stores a compound string, not a clean label, so an exact-match filter against it will never catch a real submission — only a "contains" filter does. Neither is a HubSpot limitation; both are the kind of mistake that building list logic by hand, instead of relying on a tested Workflow, makes easy to miss until you actually check the data on a real record rather than trusting the filter description.

Three HubSpot active lists — Attribution QA, MQL Slow Path, MQL Fast Path — all showing a size of zero contacts
All three lists sitting at zero contacts mid-build — not a flaw in the architecture, but two inverted filter operators that took a side-by-side property comparison to actually find.

Limitations

What I'd build next