# Revenue workflow spec: Acme Analytics (HubSpot Sales Hub and Marketing Hub Professional), excerpt

Fictional example of the redesigned spec after the audit in references/worked-example.md. The internal values are Acme's; read your own with get_properties before copying any of them.

- Version 1.0, status: proposed, effective date: 2026-11-15
- Accountable owner: RevOps lead. Approvers: marketing lead, sales lead, customer success lead, finance (billing state). Applied by: revops_admin.

## 1. Scope and motions

- Inbound demo requests (hand-raise, and demos the buyer books at submission) and trial or content leads scored to MQL. Outbound and partner motions are out of scope for this version.
- Marketing to sales: an SDR makes a relevant first attempt on a demo request within 15 business minutes, and decides within one business day whether to work an MQL.
- SDR to AE: the AE decides within one business day of a held discovery meeting whether to open a deal.
- Sales to customer success: customer success accepts a closed-won account within 2 business days, with a complete packet.
- Renewal: every negotiated or annual cycle has an owner and a next action 120 days before the decision date; self-serve monthly plans go to one billing exception owner.

## 2. Units and episodes

- Lifecycle counts a person's first qualification only.
- One HubSpot lead record per buyer action (Sales Hub Professional, leads object on): each demo request, each MQL reached on the score path and each re-qualification. The SLAs count lead records.
- How each lead is created:
  - Demo request: WORKFLOW:demo-intake creates a lead (Create record) on every submission, deduplicated on the form submission ID, with request_class hand_raise, or booked_at_submit when the buyer booked a meeting on the form. When the same request makes a fit contact an MQL, no second lead is created: this lead carries both first_response_at (hand-raise SLA) and the SDR's decision with disposition_at.
  - MQL on the score path (fit gate and engagement, no demo request): WORKFLOW:mql-scoring creates a lead with request_class mql when it sets marketingqualifiedlead. Its branch for a demo request creates nothing.
  - Re-qualification after the recycle date: WORKFLOW:mql-scoring creates a lead with request_class mql, because lifecycle cannot enter marketingqualifiedlead a second time.
  - The lead pipeline's "Create a lead" automation stays off. It would add a second lead for every demo request that also triggers the MQL, and HubSpot documents no duplicate check for it.
- Leads created by automation do not trigger the lead-associated lifecycle setting, so creating a lead never moves lifecycle.
- The default lead stages have no accept step, so the SDR's decision is its own lead property, sdr_decision (accepted or rejected, a reason required on reject). WORKFLOW:lead-disposition stamps disposition_at the first time it is set.
- Pipeline and revenue count each deal ID once. Every deal carries its buying contact.

## 3. State fields

```yaml
relationship:
  field: lifecyclestage
  values: [subscriber, lead, marketingqualifiedlead, salesqualifiedlead, opportunity, customer, evangelist]
follow_up:
  field: lead stage, default lead pipeline, one lead record per request
  values: [New, Attempting, Connected, Qualified, Disqualified]   # labels; read the stage IDs from the pipeline
  decision: sdr_decision (accepted, rejected), stamped into disposition_at on first set
  reasons: disqualification_reason, at most 12 values, including bad_timing and no_project
  stage_dates: lead_stage_changed_at, stamped by WORKFLOW:lead-disposition (lead stage calculated properties need Service Hub Professional, which Acme does not have)
  retired: hs_lead_status on contacts stays read-only for history after go-live (open decision 3)
opportunity:
  field: dealstage, pipeline new_business
  stages: [discovery, evaluation, proposal, closedwon, closedlost]
  lost_reasons: closed_lost_reason, at most 12 values, "other" under 10% a month
customer_state:
  fields: [subscription_status, latest_invoice_status, collection_method]
  writer: INTEGRATION:billing-sync (never lifecyclestage)
  paid: latest_invoice_status = paid, read from the invoice; subscription_status active is not payment
  renewal_record: renewal pipeline deal, one per negotiated or annual cycle
```

## 4. Transitions (excerpt)

```yaml
- id: T-MQL
  from: lead
  to: marketingqualifiedlead
  unit: person (first qualification); re-qualifications are counted on lead records
  evidence: fit gate (country in the served list, 50 to 2,000 employees, use case known)
    AND (engagement score >= 40 within 30 days OR a demo request)
  writer: WORKFLOW:mql-scoring (only); imports may set lead, never marketingqualifiedlead
  request_record: the demo-intake lead when a demo request set the MQL; otherwise the lead WORKFLOW:mql-scoring creates
  entry_timestamp: Date entered Marketing Qualified Lead
  exits:
    advance: the SDR sets sdr_decision accepted, then moves the lead to Qualified and sets salesqualifiedlead
    recycle: sdr_decision rejected with reason bad_timing; lead Disqualified; WORKFLOW:recycle sets recycle_date 90 days out; lifecycle untouched
    disqualify: sdr_decision rejected with a reason from the controlled list; lead Disqualified; lifecycle untouched
  correction: WORKFLOW:lifecycle-correction with correction_reason, approved by revops_admin
- id: T-OPP
  to: opportunity
  evidence: a deal exists with amount, close date and the buying contact and company associated
  writer: AUTOMATION:deal-created
- id: D-PROPOSAL
  from: evaluation
  to: proposal
  unit: opportunity
  evidence: buyer-confirmed problem, decision process and a dated next step recorded on the deal
  field: dealstage
  value: proposal
  writer: CRM_UI (the AE); the warehouse sync proposes through product summary properties only
  entry_timestamp: Date entered proposal (stage calculated properties on for every deal stage)
  exits:
    advance: closedwon with the closed-won packet gate fields filled
    lost: closedlost with closed_lost_reason
  correction: the sales manager moves the deal back with a reason
- id: T-CUSTOMER
  to: customer
  evidence: the deal reaches closedwon
  writer: AUTOMATION:deal-won (only). The billing integration and forms never set customer.
  gate: requires a prior opportunity
  correction: WORKFLOW:lifecycle-correction with correction_reason, approved by revops_admin
```

## 5. SLAs (from sla-register.yaml)

- Hand-raise: first relevant response within 15 business minutes, with escalation at 15, 30 and 60. The target is the promise on the demo page. The Q3 baseline was 32.5 business minutes over 10 due requests, with the 2 unanswered ones counted at their age (the answered-only median of 19.5 understated it); 10 due is under 30, so the promise stands and is not a proposal.
- Booked at submission: a personal confirmation within 1 business day and before the meeting (proposal). A booking never counts as a hand-raise response.
- MQL disposition: accept or reject within 480 business minutes (1 business day). The clock stops at disposition_at, stamped when sdr_decision is first set. Proposal: the Q3 baseline of 360 business minutes rests on only 5 due requests.
- SDR to AE: AE decision within 1 business day of the held meeting (proposal).
- Closed won to customer success: accepted within 2 business days, kickoff scheduled within 5 (proposals), on the customer success calendar.
- Renewal: negotiated or annual cycles owned with a next action 120 days before the decision date or 30 days before the notice deadline, whichever is earlier (proposal). Self-serve billing exceptions: actioned within 1 business day by the exception owner (proposal).
- Calendars: sdr_us (America/New_York, Monday to Friday 09:00 to 17:00) for marketing to sales and SDR to AE; cs_us (America/Chicago, 08:00 to 16:00) for customer success and renewals; holidays in workflow-config.json.

## 6. Routing summary

- Precedence: repeat delivery (same submission ID), support intent to the support queue, open opportunity owner, account owner, eligibility, segment, round robin among available SDRs, fallback to the SDR team lead.
- WORKFLOW:demo-intake gives the lead to the open opportunity or account owner when one exists, and WORKFLOW:mql-scoring gives an mql lead to the contact owner when one is set (with no contact owner, the lead is created unassigned). "Inbound lead rotation" enrolls hand_raise and mql leads and rotates only those that still have no owner: Round Robin among available SDRs, "Overwrite any existing lead owner" off, a no-owner branch to the SDR team lead, and an unowned lead view checked hourly during coverage.
- "Sync lead owner with contact or company owner" is off, and the contact-based rotation is off, so no rotation writes hubspot_owner_id.
- Every routing outcome writes routing_rule_id, routing_reason, the owner and assigned_at on the lead.

## 7. Report pack (names only)

- R01 MQL to SQL by entry cohort (30-day window), R02 stage velocity, R03 SLA compliance per handoff, R04 buyer clock split, R05 aging and unowned queue, R06 rejection and lost reasons and loops, R07 writer conflicts, R08 pipeline by source snapshot, R09 closed won to customer success acceptance, R10 renewal coverage and self-serve exceptions.

## 8. Field ownership (excerpt)

- lifecyclestage: forms set subscriber or lead; imports and users in the UI set lead (a manual move back to lead without a correction source stays a T03 WARN); WORKFLOW:mql-scoring sets marketingqualifiedlead; SDRs set salesqualifiedlead; AUTOMATION:deal-created sets opportunity; AUTOMATION:deal-won sets customer; customer success sets evangelist; only WORKFLOW:lifecycle-correction clears or sets any other value.
- Lead owner: WORKFLOW:demo-intake (open opportunity or account owner) and WORKFLOW:mql-scoring (contact owner) for relationship owners, lead rotation for the rest; the SDR team lead reassigns with a reason and received_at is kept.
- sdr_decision: SDRs; disposition_at: WORKFLOW:lead-disposition, first write wins.
- hubspot_owner_id: the relationship owner, set by the account owner rule or by hand; rotation and integrations never write it.
- dealstage and closed_lost_reason: sales in the UI only; the warehouse sync writes product summary properties instead.
- subscription_status and latest_invoice_status: INTEGRATION:billing-sync only.

## 9. Open decisions

- 1: allow discovery straight to closed won for single-call deals? Sales lead, by 2026-11-08.
- 2: was c-1006's move from customer back to lead a churn correction? revops_admin, by 2026-11-08.
- 3: the date hs_lead_status becomes read-only once lead records are live. RevOps lead, by 2026-11-15.
