# Stage model

How to split workflow state into fields, pick the unit each transition counts, and write transitions a CRM admin can build and a reviewer can test. Platform behaviour behind these rules is in [platform-mechanics.md](platform-mechanics.md).

## Four questions, four fields

Every revenue workflow has to answer four questions at any moment. Give each its own field.

- Relationship: what is this person or company to us? Lifecycle stage. It records the furthest commercial milestone reached and moves forward only.
- Follow-up: is someone working this request, and what happened? Lead status, or the stage of a request record (HubSpot lead, Salesforce Lead, Attio list entry).
- Opportunity: how far is this deal? Deal or opportunity stage, per pipeline.
- Customer state: is the subscription active and paid, and when is the next renewal decision? Subscription or contract state from billing or customer success, plus one renewal cycle record per cycle.

Why one field cannot carry all four:

- A rep marks a lead "bad timing" and a workflow sets lifecycle back to Lead. Scoring re-qualifies it a week later. The funnel counts two MQLs for one person, and the stage dates now describe the recycle, not the buyer.
- A churned customer moved back to Lead loses the record of having bought. Win-back reports, close dates and customer counts break.
- "Trial" stored in lifecycle collides with an open opportunity on the same contact, and the forward-only rule then blocks the real stage.
- Recycled, disqualified, churned, trial and in-sequence are follow-up or customer states. None belongs in lifecycle.

## Units and roll-ups

Name the unit of every transition: person, request, account, opportunity or renewal cycle.

- A funnel that changes unit states the roll-up rule. Example: "an account counts as SQL on the first date any of its contacts enters SQL; later contacts do not count again".
- Rates divide like by like. Never divide qualified accounts by leads without the roll-up rule written down.
- Report the unit beside every number in the report pack ([report-pack.md](report-pack.md)).

## Request episodes

A hand-raise (demo request, pricing question, trial signup, sales callback) is a request with its own ID and received time.

- Lifecycle counts a person's first qualification only. A second demo request six months later is real work but not a new lifecycle entry.
- Count repeat qualifications on request records or a warehouse ledger, never by moving lifecycle back.
- One request record per buyer action. An MQL reached through a hand-raise uses the hand-raise's record; only an MQL reached on the score path, or a re-qualification, gets a record of its own. In HubSpot, use either the lead pipeline's Create a lead automation or a workflow that creates leads for a trigger, never both for the same trigger ([failure-modes.md](failure-modes.md), FM26).
- Decision rule: use an episode record when any SLA is measured per request or when any person in the audit window has two or more requests. A contact-level "first contacted" timestamp is acceptable only when nobody has more than one. In design mode, without history, use an episode record whenever a known person can send a second request.

Episode record options:

- HubSpot leads object: Sales Hub Professional or Enterprise. One contact can have several leads, each with its own owner, stage and Disqualification Reason. Use either the Lead status property or the leads object for follow-up, not both for the same state. The default lead stages have no accept step, so the SDR's accept or reject goes in its own property (sdr_decision: accepted or rejected, a reason required on reject), and a workflow stamps disposition_at the first time it is set.
- Salesforce: one Lead per request (duplicate rules decide when a known person gets a new Lead), or a custom request object related to the Contact. Conversion is the sales handoff boundary.
- Attio: list entries; a list can hold several entries for the same record, each with a status attribute.
- Warehouse ledger: request_id, record_id, request_class, received_at and the clock fields, when the CRM cannot hold episodes. The audit's requests file uses exactly this shape.

## Platform mapping

HubSpot:

- Lifecycle stage lives on contacts and companies. Read option internal values with get_properties and write internal values, never labels.
- Follow-up: Lead status on the contact, or the leads object for episodes.
- Deal stages per pipeline, with stage IDs read from the deal stage property options or the CLI's pipeline listing (confirm the command name in the CLI help).
- Customer state: subscription or custom properties written by the billing writer, not lifecycle.
- Renewal: a renewal pipeline deal or a custom object, one record per cycle.

Salesforce:

- Lead Status picklist, with one value marked as converted in the LeadStatus values.
- Conversion is the sales handoff. It cannot be undone, and ConvertedDate is a date only.
- If lifecycle must span Lead and Contact, the person-level lifecycle field exists on both and is mapped in lead conversion settings.
- Opportunity stages per sales process. Customer state on the Account from billing. Renewals as renewal opportunities.

Attio:

- Status attributes on objects or lists hold stages. List entries are episodes. Historic values are readable with show_historic.

## Transition table

Each transition is one block in the spec ([../assets/workflow-spec-template.md](../assets/workflow-spec-template.md)). A filled example:

```yaml
- id: T-MQL
  from: lead
  to: marketingqualifiedlead
  unit: person (first qualification); repeats counted on lead records
  evidence: fit gate passed (country, employee range and use case known and in range)
    AND (engagement score >= 40 within 30 days OR a hand-raise request)
  exclusions: existing customer, open opportunity, partner, competitor, test, spam
  field: lifecyclestage
  value: marketingqualifiedlead
  writer: WORKFLOW:mql-scoring (only)
  entry_timestamp: Date entered Marketing Qualified Lead (read the internal name with get_properties)
  exits:
    advance: SDR accepts within 1 business day (custom lead status ACCEPTED), then SQL on qualification
    recycle: lead status BAD_TIMING with recycle_reason and recycle_date; lifecycle unchanged
    disqualify: lead status UNQUALIFIED with disqualification_reason; lifecycle unchanged
  correction: WORKFLOW:lifecycle-correction with correction_reason, approved by revops_admin
```

Deal stages get the same block. The exit evidence is what the buyer confirmed, never the rep's optimism:

```yaml
- id: D-PROPOSAL
  from: evaluation
  to: proposal
  unit: opportunity
  evidence: buyer-confirmed problem, decision process and a dated next step recorded on the deal;
    the buying contact associated (Salesforce: a contact role)
  field: dealstage
  value: proposal
  writer: CRM_UI (the AE); integrations propose through product summary properties
  entry_timestamp: Date entered proposal (stage calculated properties on for every stage)
  exits:
    advance: closedwon with the closed-won packet gate fields filled
    lost: closedlost with closed_lost_reason from a list of at most 12 values; "other" under 10% a month
  correction: sales manager moves the deal back with a reason
```

Rules for every block:

- Evidence is a committed fact: sales recorded acceptance, a deal exists with amount and close date, billing reports a paid invoice. An email open, a page view or a booked meeting alone is never evidence.
- One writer per transition, named by its source label. A second automated writer needs written precedence or it is a FAIL.
- The entry timestamp is a field the CRM maintains, not one a workflow copies.
- Every transition has advance, recycle and disqualify exits, or says why one does not apply.

## Illustrative B2B transition set

Illustrative only; adapt the evidence to the team's motion.

- Subscriber: confirmed subscription to a named list. Writer: the form. Unit: person.
- Lead: a request or conversion beyond subscribing (guide, webinar, trial). Writer: the form or the intake workflow.
- MQL: fit gate and (engagement threshold or hand-raise). Writer: the scoring workflow only.
- Accepted (SAL): an explicit accept with a timestamp, held in the follow-up layer (a custom lead status value such as ACCEPTED, or an sdr_decision property on a lead record), not in lifecycle. Writer: the SDR.
- SQL: qualification evidence recorded by sales (problem, timing, buying role confirmed). Writer: sales, in the UI or the lead stage Qualified.
- Opportunity: a deal exists with amount, close date and the right contact and company associations. Writer: the deal-created automation.
- Customer: closed-won commercial evidence, or the paid billing state the spec names. Writer: the deal-won automation only.
- Evangelist: a written advocacy criterion (case study signed, referral made). Writer: customer success.

If the team needs SAL as a reporting milestone in lifecycle, add it only when the five conditions in the last section hold.

## Hand-raise bypass and exclusions

- A hand-raise skips the score, never the exclusions.
- Existing customer: route to the account owner or customer success, never to new-business rotation.
- Open opportunity: route to the opportunity owner and notify them.
- Partner: route to the partner manager with the partner ID.
- Competitor: disqualify with reason competitor; no outreach.
- Test and spam: exclude with a reason so they leave SLA denominators visibly.
- Each exclusion names its path and owner in the spec. An exclusion with no path is a dropped request.

## Recycle and disqualification

- Reasons come from a controlled list of at most 12 values, for rejections and for closed-lost deals alike. If "other" exceeds 10% of rejections or losses in a month, rewrite the list.
- Recycle sets the follow-up status, a reason and a recycle date. Lifecycle does not move.
- A recycled record re-enters only on a new request or new evidence dated after the recycle date. Engagement inputs need windows, so old activity cannot re-qualify a record.
- More than 2 entries into the same stage within any 90 days is a loop to review (T10).

## Backward moves and the correction path

- HubSpot: one governed correction workflow (clear, then set) with a correction_reason property and a named approver. Manual reversals by reps show up as T03 WARN until the team decides who may correct.
- Salesforce: conversion cannot be undone. Correct the Contact, Account or Opportunity instead, and record the reason on the record.
- Attio: change the status through the normal update path; never use the write attribute values endpoints.
- After any correction, review stage dates, tasks, emails and reports that reacted to the wrong value.

## Product-qualified, outbound and expansion motions

- PQL: a separate signal property with observed_at, reason and expiry. A PQL may create a request episode. It never sets lifecycle by itself.
- Outbound: sequence and touch states live in the follow-up field. Lifecycle changes only when the outbound conversation produces evidence.
- Expansion: an expansion pipeline owned by the account owner. Lifecycle stays Customer. A customer's new request routes to the account owner first.

## Account-based qualification and published frameworks

- Forrester's B2B Revenue Waterfall moved from leads to buying groups and opportunities.
- Winning by Design's Bowtie continues the funnel past the sale into onboarding, adoption and expansion. Use it as vocabulary for the right side of the workflow, which this skill covers with handoff contracts and renewal cycle records. Never copy a framework's stage names into lifecycle stage.
- The cost is a data model change: contact roles on accounts, buying group membership and opportunity contact roles kept current. Without that data, account-level stages are guesses.
- Start with the person and request model and report an account roll-up. Move to an account or buying-group unit when at least half of last quarter's won deals had three or more contact roles and those roles are maintained.

## When a custom lifecycle stage is justified

Add one only when all five hold:

- It marks a distinct milestone every team shares.
- It has a named owner.
- It has written entry evidence.
- A report uses it.
- The CRM maintains its timestamps (an option on the built-in lifecycle property, not a separate custom property).

Never replace the default Customer stage with a custom one in HubSpot: the automatic contact Close date depends on it.
