tomgranot/hubspot-admin-skills/build-smart-lists

Claude Code and agent skill by tomgranot/hubspot-admin-skills

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Create foundational segmented lists for marketing and sales operations via the Lists API, plus advanced UI-built segments: a master sendable list, ICP-based lists, persona lists, and engagement lists. All active (dynamic) lists.

Install

npx vskill@latest install tomgranot/hubspot-admin-skills/build-smart-lists

SKILL.md

Build Smart Lists for Segmentation

Create the core active (dynamic) lists that serve as the foundation for all marketing campaigns, sales prioritization, and database health monitoring. Ten foundation lists are created automatically via the Lists API (v3); a further set of advanced segments is built in the UI.

Why This Matters

Without predefined lists, every email campaign requires building filters from scratch, there is no standardized definition of "who can we actually email", and there is no persona-based segmentation. The marketing team cannot quickly answer basic questions like "How many senior decision-makers can we email right now?" or "How many engaged contacts do we have?"

Prerequisites

  • Super Admin or Marketing Hub Admin permissions
  • A HubSpot service key (HUBSPOT_ACCESS_TOKEN in .env; an existing legacy private app token also works) with crm.lists.read and crm.lists.write scopes
  • Python 3.10+ with `uv`
  • ICP Tier property created and workflows processed (create-icp-tiers skill) — the script assumes the property name company_segment with values like tier_1_primary_icp; adjust its configuration block if yours differ
  • Lead scoring model created (build-lead-scoring skill) is recommended but not required
  • Lifecycle Stage property populated for customers and partners (fix-lifecycle-stages skill)

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/build-smart-lists/ in a clone of the repo).

StageScriptRun with
Execute`scripts/execute.py`uv run scripts/execute.py

execute.py creates the 10 foundation lists below via POST /crm/v3/lists, skips lists that already exist (409), and writes a CSV audit trail of created list IDs. There are no before/after scripts: the before-state inventory is a single Lists API query (see Before), and verification is a member-count review in the UI.

Interview: Gather Requirements

Before executing, collect the following information from the user:

Q1: What defines "engaged" for your business? (e.g., activity in last 60-120 days)

  • Examples: Email open or click in last 90 days, website visit in last 60 days, form submission in last 30 days
  • Default: Email open or click in the last 90 days (set ACTIVE_WINDOW_DAYS in the script; shorter cycles suit high-velocity sales, longer cycles suit enterprise)

Q2: What job titles represent your target personas?

  • Examples: CEO, COO, CFO, CTO, CRO, VP of Operations, VP of Marketing, Director of Operations, Director of Marketing, Head of Procurement, Engineering Manager
  • Default: C-suite and VP/Director-level leaders across business functions (used for the UI-built persona list)

The List Library

Foundation lists (created by the script)
#List NameObjectKey Filter
1All Marketing ContactsContactshs_marketable_status = true
2All LeadsContactsLifecycle stage = Lead
3All MQLsContactsLifecycle stage = MQL
4All SQLsContactsLifecycle stage = SQL
5All CustomersContactsLifecycle stage = Customer
6ICP Tier 1 CompaniesCompaniesICP Tier = Tier 1
7ICP Tier 2 CompaniesCompaniesICP Tier = Tier 2
8ICP Tier 3 CompaniesCompaniesICP Tier = Tier 3
9Engaged Last 90 DaysContactsEmail open OR click within the active window
10Unengaged 180+ DaysContactsMarketable + no email open beyond the re-engagement window
Advanced segments (built in the UI)

These use features that benefit from human judgment and portal-specific configuration (list-membership filters, persona keywords, form-name keywords):

#List NamePurposeKey Filters
AMarketable - ActiveMaster sendable list (who CAN receive email)Marketing contact + not unsubscribed + not bounced + has email + not quarantined
BICP Tier 1 ContactsHighest priority contactsAssociated company ICP Tier = Tier 1 + member of Marketable - Active
CICP Tier 2 ContactsSecondary priority contactsAssociated company ICP Tier = Tier 2 + member of Marketable - Active
DPartnersPartner communications and exclusionLifecycle stage = Partner (or custom contact type property)
ERe-engagement NeededSunset candidates5+ emails delivered + no open in 180 days + member of Marketable - Active
FSenior Decision MakersTop persona listJob title contains target titles
GIndustry LeadersContacts at companies in target verticalsAssociated company industry is any of target industries
HContent EngagedForm submissions and content downloadsForm submissions > 0 OR conversion contains content keywords

Overlap note: "All Customers" (script) covers the Customers use case; "Unengaged 180+ Days" (script) is a simpler variant of "Re-engagement Needed" (UI, adds the delivered-count guard). Keep whichever fits your operation — do not maintain both without a reason.

Plan

  1. Run the interview above; set ACTIVE_WINDOW_DAYS, REENGAGEMENT_WINDOW_DAYS, and ICP_PROPERTY_NAME in the script's configuration block
  2. Decide which advanced segments to build in the UI
  3. Create foundation lists via the script, advanced segments via the UI
  4. Verify list sizes make sense relative to the database

Before

Inventory existing lists to avoid duplicates (POST /crm/v3/lists/search with an empty query returns all lists, paginated). Check for name collisions with the library above — the script skips exact name matches (HTTP 409), but near-duplicates ("Customers" vs "All Customers") need a human decision: merge, rename, or skip.

Execute

Step 1: Foundation lists (script)
bash
uv run scripts/execute.py

The script posts each definition to POST /crm/v3/lists with processingType: "DYNAMIC". Example payload shape (see the script for all ten):

python
{
"name": "All Customers",
"objectTypeId": "0-1", # contacts ("0-2" for companies)
"processingType": "DYNAMIC",
"filterBranch": {
"filterBranchType": "AND",
"filterBranches": [],
"filters": [{
"filterType": "PROPERTY",
"property": "lifecyclestage",
"operation": {
"operationType": "ENUMERATION",
"operator": "IS_EQUAL_TO",
"value": "customer",
},
}],
},
}

If the Lists API returns 403 on your plan tier, build the same lists manually in the UI using the table above.

Step 2: Marketable - Active (master sendable list — UI)

This is the most important advanced list. It defines the single source of truth for "who can receive marketing email." All campaign sends should reference this list.

  1. Go to Contacts > Lists > Create list
  2. Select Contact-based > Active list
  3. Name: Marketable - Active
  4. Add filters (all AND logic):
  • Marketing contact status > is any of > Marketing contact
  • AND Unsubscribed from all email > is not equal to > True
  • AND Hard bounce reason > is unknown
  • AND Email > is known
  • AND Email quarantined > is not equal to > True
  1. Save the list
Step 3: Remaining advanced segments (UI)

ICP Tier 1 / Tier 2 Contacts (B, C):

  • Filters: Associated company property > ICP Tier > is any of > [tier] AND List membership > is member of > Marketable - Active
  • Using List membership as a filter is a powerful pattern: these lists automatically inherit all deliverability and consent logic from Marketable - Active. New disqualification conditions added there propagate automatically.

Partners (D): Lifecycle stage > is any of > Partner. Always exclude from prospect campaigns.

Re-engagement Needed (E):

  • Marketing emails delivered > is greater than > 5
  • AND Last marketing email open date > is more than > 180 days ago (tune to your cycle: 120-270)
  • AND List membership > is member of > Marketable - Active
  • Feeds the engagement-based suppression workflow.

Senior Decision Makers (F): Job title > contains any of > [your target titles]. Customize keywords to your buyer personas.

Industry Leaders (G): Associated company property > Industry > is any of > [your target industries].

Content Engaged (H): OR logic between groups:

  • Number of Form Submissions > is greater than > 0
  • OR First conversion > contains any of > [content keywords like "Download", "Guide", "Checklist", "E-Book", "Whitepaper"]
  • OR Recent conversion > contains any of > [same keywords]
  • The conversion-based filters depend on your form naming conventions — review actual form names (Marketing > Forms) and adjust.

After

Verify List Sizes

After all lists are created and processed:

ListExpected RangeRed Flag If...
All Marketing Contacts / Marketable - Active30-80% of total contactsBelow 10% (too many excluded) or above 90% (filters too loose)
ICP Tier lists2-10% of marketable0 (ICP Tier not populated)
Engaged (active window)5-30% of total contactsBelow 2% (possible engagement tracking issue)
All CustomersKnown customer countOff by more than 20% from expected
PartnersKnown partner countOff by more than 20% from expected
Unengaged / Re-engagement Needed10-40% of marketableAbove 60% (possible date threshold issue)
Senior Decision Makers5-25% of total contacts0 (job title data missing)
Industry Leaders10-50% of total contacts0 (industry data missing)
Content Engaged1-10% of total contacts0 (no form submissions or wrong keywords)
Verification Checklist
  1. All lists show as Active (not Static) in the list view
  2. All lists have completed processing (no "Processing" status)
  3. Sendable-list sanity check: Open Marketable - Active (or All Marketing Contacts), click 5 random contacts. Each should have a valid email, not be unsubscribed, not be bounced.
  4. ICP list check: Open an ICP Tier 1 list, click 5 records. Each should have ICP Tier = Tier 1.
  5. Persona list check: Open Senior Decision Makers, verify job titles match expected patterns. Watch for false positives (e.g., "Marketing Intern" matching on "Marketing").
  6. Re-engagement check: Verify contacts have 5+ emails delivered and no open beyond your configured window.

Rollback

  • Lists can be deleted via DELETE /crm/v3/lists/{listId} or the UI — the script's CSV audit trail records every list ID it created.
  • Deleting a list does not affect the contacts in it — only the list definition is removed.
  • Check whether any workflows, emails, or dependent lists (list-membership filters!) reference a list before deleting it.

Key Technical Learnings

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
78
Repository forks
18
7-day trend score
20

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/tomgranot/hubspot-admin-skills/build-smart-lists/badge)](https://verified-skill.com/skills/tomgranot/hubspot-admin-skills/build-smart-lists)

Something wrong with this skill or its page? Tell us.