fix-lifecycle-stages
Ensure all contacts and companies have appropriate lifecycle stages.
Skill by tomgranot
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/tomgranot-fix-lifecycle-stages
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: backend
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Python package
Network hosts it talks to 2
api.hubapi.com· found in codecodeload.github.com· found in install command
Files it touches
~/.claude/skills/fix-lifecycle-stages
What's inside
- Skill name
fix-lifecycle-stages- Description
- Ensure all contacts and companies have appropriate lifecycle stages. Backfills missing stages via API, fixes records stuck at disallowed stages, and creates prevention workflows to stop future gaps.
- License
- MIT
- Metadata
- author: tomgranot · version: 1.1 · category: data-enrichment
- Instructions
- 231 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
- Instructions call the network
Files (4)
- SKILL.md
- after.py · scripts/
- before.py · scripts/
- execute.py · scripts/
SKILL.md (first lines)
# Fix Lifecycle Stages
Ensure every contact and company has an appropriate lifecycle stage. This includes backfilling missing stages, correcting disallowed stage values, and creating prevention workflows that automatically assign stages to new records.
## Why This Matters
Records without a lifecycle stage are invisible in pipeline reports, excluded from stage-based workflows, and cannot be properly segmented. Even a small percentage of missing lifecycle stages corrupts funnel reporting and makes pipeline analytics unreliable. Lifecycle stage data is also a prerequisite for lead scoring models and lifecycle progression workflows.
## Prerequisites
- Phase 1 hygiene processes completed (invalid/deleted contacts removed first)
- A HubSpot service key (`HUBSPOT_ACCESS_TOKEN` in `.env`; an existing legacy private app token also works) with contact and company read/write scopes
- Python 3.10+ with [`uv`](https://github.com/astral-sh/uv)
- Access to Contacts and Companies with bulk edit permissions
- Access to Automation > Workflows
- Understanding of HubSpot's lifecycle stage progression rules (see Critical Concept below)
## Scripts
Script paths are relative to this skill's directory. Run them from your project directory so `.env`, `data/`, and `reports/` resolve there: prefix each path with wherever this skill is installed (`skills/fix-lifecycle-stages/` in a clone of the repo).
| Stage | Script | Run with |
|-------|--------|----------|
| Before | [`scripts/before.py`](./scripts/before.py) | `uv run scripts/before.py` |
| Execute | [`scripts/execute.py`](./scripts/execute.py) | `uv run scripts/execute.py` |
| After | [`scripts/after.py`](./scripts/after.py) | `uv run scripts/after.py` |
`before.py` audits missing and per-stage counts for contacts and companies. `execute.py` performs the clear-then-set fixes (review its configuration block for the disallowed-stage mapping before running). `after.py` verifies coverage.
## Critical Concept: Forward-Only Lifecycle Progression
**HubSpot has forward-only lifecycle progression by default.** The built-in order is:
Subscriber > Lead > MQL > SQL > Opportunity > Customer > Evangelist
To move a record from a later stage (e.g., "Other", "Evangelist") to an earlier one (e.g., "Lead"), you must:
1. **FIRST** clear the lifecycle stage (set to blank/empty)
2. **THEN** set the new value
A direct set to an earlier stage will be **silently rejected** — no error, no warning, the value simply does not change. This is the single most common gotcha when fixing lifecycle stages.
```python
url = f"{BASE}/crm/v3/objects/contacts/{contact_id}"
# WRONG — silently fails if current stage is "later" than target
requests.patch(url, headers=HEADERS,
json={"properties": {"lifecyclestage": "lead"}})
# CORRECT — clear first, then set (two separate calls)
requests.patch(url, headers=HEADERS,
json={"properties": {"lifecyclestage": ""}})
requests.patch(url, headers=HEADERS,
json={"properties": {"lifecyclestage": "lead"}})
```
## Plan
1. Audit missing and disallowed lifecycle stages (before state)
2. Define which stages are "disallowed" for your business and map them to correct stages
3. Fix contacts with disallowed stages (clear + re-set)
4. Set missing stages to appropriate defaults based on associated company context
5. Create prevention workflows for contacts and companies
6. Verify 100% coverage (after state)
## Before
### Audit Script
Run `uv run scripts/before.py`. The core queries it runs:
```python
def count(object_type, filters):
resp = requests.post(f"{BASE}/crm/v3/objects/{object_type}/search",
headers=HEADERS,
json={"filterGroups": [{"filters": filters}], "limit": 1})
resp.raise_for_status()
return resp.json()["total"]
missing = count("contacts", [
{"propertyName": "lifecyclestage", "operator": "NOT_HAS_PROPERTY"}])
print(f"Contacts missing lifecycle stage: {missing}")
stages = ["subscriber", "lead", "marketingqualifiedlead", "salesqualifiedlead",
"opportunity", "customer", "evangelist", "other"]
for stage in stages:
n = count("contacts", [
{"propertyName": "lifecyclestage", "operator": "EQ", "value": stage}])
if n:
print(f" {stage}: {n}")
print(f"Companies missing lifecycle stage: "
f"{count('companies', [{'propertyName': 'lifecyclestage', 'operator': 'NOT_HAS_PROPERTY'}])}")
```
### Define Disallowed Stages
Decide which lifecycle stage values should not exist in your database. The table below shows **common examples** -- your disallowed stages and their correct mappings will depend on how your organization uses the CRM. Review your own stage distribution and decide what makes sense for your business:
| Example Disallowed Stage | Common Reason | Example Correct Stage |
|--------------------------|---------------|----------------------|
| (empty/blank) | Invisible to reports | Lead (default) |
| Subscriber | Often misapplied when not used for newsletter-only contacts | Lead |
| Other | Meaningless catch-all | Lead |
| Evangelist | Rarely used correctly in most organizations | Customer (if actual customer) or Lead |
**These are starting-point examples only.** Your mapping will differ based on your sales process, integrations, and how stages are currently used. Define your specific mapping before executing.
## Execute
### Step 1: Fix Contacts at Disallowed Stages
For contacts at "Subscriber", "Other", or "Evangelist" that should be moved to "Lead":
```python
# Pattern: Clear then set (required for backward movement)
DISALLOWED_TO_LEAD = ["subscriber", "other", "evangelist"]
for stage in DISALLOWED_TO_LEAD:From TomGranot/hubspot-admin-skills, MIT.
More from TomGranot/hubspot-admin-skills
- 0.675score
sandbox-self-test
tomgranot
Verify the entire toolkit against a disposable HubSpot developer test account or sandbox that you bring: seed synthetic fixtures, run every scripted skill's…
SkillAFreshness: A★ 77Claude Code +9 - 0.675score
hubspot-audit
tomgranot
Run a full, read-only HubSpot portal audit covering both the marketing and sales sides: database, deliverability, engagement, owners, pipelines, deal quality…
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
create-icp-tiers
tomgranot
Classify companies into Ideal Customer Profile (ICP) tiers based on firmographic data (industry + employee count).
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
suppress-global-unsubscribes
tomgranot
Identify and suppress globally unsubscribed contacts to ensure legal compliance and reduce billing.
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
new-contact-hygiene-workflow
tomgranot
Build a HubSpot workflow that auto-enriches and stages new contacts upon creation.
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
merge-duplicate-companies
tomgranot
Identify duplicate company records by domain and name, export audit CSVs for review, and guide merging.
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
bounce-monitoring-workflow
tomgranot
Build a workflow to protect sender reputation through automated bounce monitoring.
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
lifecycle-progression-workflow
tomgranot
Build workflows to automate contact progression through the sales funnel: Lead to MQL to SQL to Opportunity to Customer.
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
build-smart-lists
tomgranot
Create foundational segmented lists for marketing and sales operations via the Lists API, plus advanced UI-built segments: a master sendable list, ICP-based…
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
cleanup-deals
tomgranot
Find past-due, stale, incomplete, unexplained-loss, and likely test deals, then route each owner's flagged deals to them as one review task.
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
assign-unowned-contacts
tomgranot
Assign an owner to marketing contacts that have no owner.
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
delete-no-email-contacts
tomgranot
Delete contacts with no email address from a HubSpot CRM instance.
SkillAFreshness: A★ 77Claude Code +9
Signals
- Stars
- 77
- Forks
- 18
- Open issues
- 0
- Last commit (this folder)
- Oct 12, 2026 (17 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- Version
- 1.1
- License
- MIT
- Website
- hubspot.granot.io
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from tomgranot
- 0.675score
hubspot-audit
tomgranot
Run a full, read-only HubSpot portal audit covering both the marketing and sales sides: database, deliverability, engagement, owners, pipelines, deal quality…
SkillAFreshness: A★ 77Claude Code +9 - 0.675score
sandbox-self-test
tomgranot
Verify the entire toolkit against a disposable HubSpot developer test account or sandbox that you bring: seed synthetic fixtures, run every scripted skill's…
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
merge-duplicate-companies
tomgranot
Identify duplicate company records by domain and name, export audit CSVs for review, and guide merging.
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
suppress-global-unsubscribes
tomgranot
Identify and suppress globally unsubscribed contacts to ensure legal compliance and reduce billing.
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
assign-unowned-contacts
tomgranot
Assign an owner to marketing contacts that have no owner.
SkillAFreshness: A★ 77Claude Code +9 - 0.672score
lifecycle-progression-workflow
tomgranot
Build workflows to automate contact progression through the sales funnel: Lead to MQL to SQL to Opportunity to Customer.
SkillAFreshness: A★ 77Claude Code +9
Cite this page
fix-lifecycle-stages skill. Agent Mart. https://agentmart.dev/skills/tomgranot-fix-lifecycle-stages (last updated 2026-10-24)