Audience Targeting
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.
- Skill ID
- hubspot/agent-cli-skills/audience-targeting
- Publisher
- hubspot
- Repository
- agent-cli-skills
- Installs
- 0
- Files
- 2
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
hubspot/agent-cli-skills/audience-targetingInstalls these files- SKILL.md
- resources/contact-segmentation-filters.md
What this skill tells the agent
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
--filterflag = one AND group:--filter "lifecyclestage=lead AND !hubspot_owner_id". - Multiple
--filterflags are OR'd. Use for enum-OR-enum. - Operators:
=,!=,>,>=,<,<=,~(CONTAINS_TOKEN — whole-word, NOT substring). - HAS_PROPERTY: bare
nameorname?. NOT_HAS_PROPERTY:!name. Dates:YYYY-MM-DD.
~ 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 aren't exposed by properties get; discover with 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
# 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 in
hubspot objects search --type contacts \
--filter "country=United States AND hs_email_optout!=true" \
--properties email,state,cityMore 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":"...","type":"company_to_contact"}, feeding directly into a single batched objects get.
# 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 -u
hubspot 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.
while read -r cid; do hubspot associations list --from "companies:$cid" --to contacts; done \
< <(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-out
jq -c 'select(.properties.hs_email_optout != "true")' target_contacts.jsonl > campaign_audience.jsonlSaving and reusing a segment
A segment is a JSONL file. Re-use for updates, exports, or re-fetches:
# Save
hubspot 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 later
jq -c '{id}' segments/opted_in_leads.jsonl \
| hubspot objects get --type contacts --properties email,lifecyclestage,hs_lead_statusDestructive ops on a saved segment follow the dry-run → digest → confirm flow in bulk-operations/SKILL.md.
Known limits
- No Lists API surface. Can't save as a HubSpot list or filter by list membership.
~is token-match, not substring. No regex operator.properties getdoes not return enum options — discover viaobjects list+jq.associations listhas no batch--from. Loop to gather IDs, batch the downstreamobjects get.- For >100 results, use the pagination loop in
bulk-operations/SKILL.md.
More from hubspot
Use this skill in a project
Open any RiverX project, search the Skills panel for hubspot/agent-cli-skills/audience-targeting, and install it in one click.
