Skip to content
Agentmart
Submit a tool

fix-lifecycle-stages

Ensure all contacts and companies have appropriate lifecycle stages.

Skill by tomgranot

IndexedMIT★ 77
Write a review

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.

We use your email only for this newsletter. Privacy

About

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.

Use cases: backend

No 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 code
  • codeload.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

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
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from tomgranot

Cite this page

fix-lifecycle-stages skill. Agent Mart. https://agentmart.dev/skills/tomgranot-fix-lifecycle-stages (last updated 2026-10-24)