Audience Targeting

hubspot/agent-cli-skills/audience-targeting

by hubspota8eea0880838No license27 starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 7 days ago

Build a targeted contact segment by filtering on lifecycle, engagement, jobtitle, geography, or firmographics — then export it as JSONL for a campaign or downstream tool.

Instructions onlyMarketing & Sales
AI-generated overview

Builds targeted HubSpot contact segments with filter expressions and exports them as JSONL for campaigns or tools.

What it does
This skill guides the agent through constructing HubSpot contact segments using filter expressions on lifecycle stage, engagement, job title, geography, and firmographics. It documents filter syntax, operators, and CRM search API limits, then shows how to export matching contacts as JSONL files. It also covers cross-object traversal from companies to their associated contacts, saving segments as HubSpot lists or views, and reusing saved segments for updates or re-fetches.
When to use it
Use it when you need to define an audience of contacts in HubSpot, such as recent leads, decision-makers, engaged contacts, or contacts at companies in a given industry. It fits campaign preparation, prospect list building, and exporting a segment for a downstream tool.
Requirements
Requires the HubSpot CLI and authenticated access to a HubSpot portal, plus jq for JSONL processing. Some related read-only signals require OAuth login rather than a service key. The skill ships no scripts; it is instructions only, with one reference file of filter recipes.

Foundation

Read bulk-operations/SKILL.md first — pagination, JSONL piping, destructive-op safety. Reshape recipes in bulk-operations/resources/json-patterns.md. Resource: resources/contact-segmentation-filters.md is the filter-expression cookbook (lifecycle, lead status, email engagement, activity, deals, owner).

Filter syntax cheat sheet

Source of truth: hubspot objects search --help.

  • One --filter flag = one AND group: --filter "lifecyclestage=lead AND !hubspot_owner_id".
  • Multiple --filter flags are OR'd. Use for enum-OR-enum.
  • Operators: =, !=, >, >=, <, <=, ~ (CONTAINS_TOKEN — whole-word, NOT substring), @= (IN), @!= (NOT_IN).
  • HAS_PROPERTY: bare name or name?. NOT_HAS_PROPERTY: !name. Dates: YYYY-MM-DD.
  • Limits (CRM search API): max 5 --filter flags, max 6 conditions per flag, max 18 conditions total. Prefer @= / @!= for enum lists — lifecyclestage@=lead,customer is one condition, --filter "lifecyclestage=lead" --filter "lifecyclestage=customer" is two groups.
  • "None of these, or empty" needs two groups — @!= skips records where the property is unset, so OR in a !name group and repeat the shared conditions in both: --filter "hubspot_owner_id=123 AND lifecyclestage@!=customer,evangelist" --filter "hubspot_owner_id=123 AND !lifecyclestage"

~ gotcha: jobtitle~director matches the token "director", not arbitrary substrings. No regex operator — search broadly, post-filter with jq.

Properties this skill turns on

Full live list: hubspot properties list --type contacts. Enum options: hubspot properties options-list --type contacts <name> | jq -r '.value'. As a fallback (e.g. to see which values are actually in use), read live records: hubspot objects list --type contacts --properties <name> --limit 100 --format json | jq -r '.data[].properties.<name> // empty' | sort -u.

Core fields used here: lifecyclestage, hubspot_owner_id (bare/! for owned/unowned; hubspot owners list for IDs), hs_email_optout (!=true excludes opted-out), hs_email_last_open_date / notes_last_contacted (recency), jobtitle / country / city (string = or ~), num_associated_deals (0 net-new, >=1 has-pipeline).

Firmographics (industry, numberofemployees, annualrevenue) live on companies — see cross-object section.

Common segments

bash
# Recent leads (this quarter, not yet owned)hubspot objects search --type contacts \  --filter "lifecyclestage=lead AND createdate>2026-01-01 AND !hubspot_owner_id" \  --properties email,firstname,lastname,createdate
# Decision-makers by jobtitle (OR across tokens)hubspot objects search --type contacts \  --filter "jobtitle~director" --filter "jobtitle~vp" --filter "jobtitle~chief" \  --properties email,jobtitle,company
# Engaged but not yet MQL (opened recently, still lead, opted in)hubspot objects search --type contacts \  --filter "lifecyclestage=lead AND hs_email_last_open_date>2026-04-01 AND hs_email_optout!=true" \  --properties email,firstname,hs_email_last_open_date
# Geographic — US contacts opted inhubspot objects search --type contacts \  --filter "country=United States AND hs_email_optout!=true" \  --properties email,state,city

More patterns (lead status, deals, owners, combined AND/OR) in resources/contact-segmentation-filters.md.

Cross-object: companies-in-industry → their contacts

industry/numberofemployees/annualrevenue live on the company. Build the company set, then traverse — never xargs -I{} hubspot objects get per company. associations list emits {"id":"...","labels":[...],"associationTypes":[...]} — {id} feeds directly into a single batched objects get.

bash
# Step 1: target companies. Industry options are portal-specific — discover with:#   hubspot objects list --type companies --properties industry --limit 100 --format json \#   | jq -r '.data[].properties.industry // empty' | sort -uhubspot objects search --type companies \  --filter "industry=SOFTWARE AND numberofemployees>=100" \  --properties name,industry,numberofemployees \  > target_companies.jsonl
# Step 2: gather association IDs (associations list has no batch --from), then ONE batched# objects get for all contacts. Page each company with --limit/--after — a company with >100# associated contacts truncates silently on a single call.while read -r cid; do  after=""  while :; do    page=$(hubspot associations list --from "companies:$cid" --to contacts --limit 100 ${after:+--after "$after"} --format json)    echo "$page" | jq -c '.data[]?'    after=$(echo "$page" | jq -r '.meta.next // empty')    [ -z "$after" ] && break  donedone \  < <(jq -r '.id' target_companies.jsonl) \| jq -c '{id}' | sort -u \| hubspot objects get --type contacts --properties email,firstname,jobtitle,hs_email_optout \> target_contacts.jsonl
# Optional: drop opted-outjq -c 'select(.properties.hs_email_optout != "true")' target_contacts.jsonl > campaign_audience.jsonl

Saving and reusing a segment

A segment is a JSONL file. Re-use for updates, exports, or re-fetches:

bash
# Savehubspot objects search --type contacts \  --filter "lifecyclestage=lead AND hs_email_optout!=true" \  --properties email,firstname,lastname,jobtitle \  > segments/opted_in_leads.jsonl
# Assign owner (dry-run first per bulk-operations/SKILL.md)jq -c '{id, properties:{hubspot_owner_id:"12345"}}' segments/opted_in_leads.jsonl \| hubspot objects update --type contacts --dry-run
# Re-fetch with different properties laterjq -c '{id}' segments/opted_in_leads.jsonl \| hubspot objects get --type contacts --properties email,lifecyclestage,hs_lead_status

Destructive ops on a saved segment follow the dry-run → digest → confirm flow in bulk-operations/SKILL.md.

Save as a HubSpot list or view

A JSONL segment is portable, but you can also persist an audience in HubSpot:

  • List — hubspot segments create saves a list; segments get / list inspect it; segments members-list reads members; segments members-add / members-remove manage membership. Use segments update-filters to set a dynamic list's filter criteria.
  • View — hubspot views create --type <t> --name "..." --columns a,b [--filters-file filters.json] [--sort prop:asc] saves a filter set as a reusable object view; views list / get / update / replace-field / delete manage it.
  • Size it first — hubspot objects count --type contacts --filter "..." returns {"object_type":"contacts","total":N} without paging, so you can size an audience before saving or exporting it.

Related read-only signals (context for targeting)

  • hubspot buyer-intent — visiting-company intent signals (read-only). Requires OAuth login (hubspot auth login); service keys are NOT supported.
  • hubspot analytics traffic sources — where sessions/contacts came from, for source-based segmentation.
  • hubspot marketing forms list — form definitions, to segment by which form a contact converted on.

Run each command's --help for the full surface.

Known limits

  • ~ is token-match, not substring. No regex operator.
  • Enum options: hubspot properties options-list --type contacts <name> (fallback: read live records via objects list + jq).
  • associations list has no batch --from, and returns one page (default 100) per call. Loop to gather IDs and page each call with --limit/--after until .meta.next is null, then batch the downstream objects get — otherwise a company with >100 associated contacts truncates silently.
  • For >100 results, use the pagination loop in bulk-operations/SKILL.md.

Source and attribution

Source:hubspot/agent-cli-skillsinaudience-targetingat commita8eea08

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal

Audience Targeting · audience-targeting Agent Skill | SourceWeft