npx skills add ...
npx skills add hubspot/agent-cli-skills --skill sales-reporting
Daily briefings, pipeline snapshots, and win/loss analysis from the terminal — closing-this-week, open pipeline by stage/owner, and closed-won vs closed-lost over a period.
npx skills add hubspot/agent-cli-skills --skill sales-reporting
hubspot <command> --help is authoritative. Build on bulk-operations/SKILL.md — JSONL shape, batch-read rules, and pagination live there. Reshape patterns: bulk-operations/resources/json-patterns.md. search/list cap at 100 rows per call; a result of exactly 100 is almost always truncated — paginate via bulk-operations/SKILL.md before aggregating.
hs_is_closed_won is returned as "true"/"false" (string); amount is a numeric string. Use tonumber for arithmetic; compare booleans as strings (== "true") when filtering client-side.--filter expressions, hs_is_closed_won=true and hs_is_closed!=true work — the API parses the value.--properties returns the standard nested shape: {"id":"123","properties":{"amount":"5000","dealname":"..."}}. Reference fields as .properties.amount in jq.dealstage are portal-specific. Map them with hubspot pipelines stages --type deals --pipeline <id>.hubspot_owner_id is a numeric string. Resolve to a name with hubspot owners list (fields: id, firstName, lastName, email).Date windows differ between macOS and GNU date:
Deals closing in the next 7 days:
Deals updated in the last 24h:
Open-pipeline summary line:
By stage — count and amount per dealstage:
By owner:
To label owner IDs with names, dump the owners file once and join:
Filter on hs_is_closed_won=true for won; hs_is_closed=true AND hs_is_closed_won!=true for lost. Scope with closedate>=YYYY-MM-DD AND closedate<YYYY-MM-DD.
Closed won / lost in a period:
Win rate by rep — pull all closed deals in the period, group, divide. Note: hs_is_closed_won lands as a string, so compare == "true".
Revenue by close month (won deals):
hubspot pipelines stages does not expose stage probability — won/lost stages can't be auto-identified from the stages list. Use hs_is_closed_won on deals instead.hubspot_owner_id and resolve names from hubspot owners list client-side.hubspot objects search --type deals \
--filter "closedate>$TODAY AND closedate<$NEXT_7 AND hs_is_closed!=true" \
--properties dealname,amount,closedate,hubspot_owner_idhubspot objects search --type deals \
--filter "hs_lastmodifieddate>$YESTERDAY AND hs_is_closed!=true" \
--properties dealname,amount,dealstage,hs_lastmodifieddatehubspot objects search --type deals --filter "hs_is_closed!=true" --properties amount \
| jq -rs '{count: length, value: ([.[].properties.amount | select(. != null) | tonumber] | add // 0 | round)}
| "Open pipeline: \(.count) deals, $\(.value)"'hubspot objects search --type deals --filter "hs_is_closed!=true" \
--properties dealstage,amount \
| jq -rs '
group_by(.properties.dealstage)
| map({stage: .[0].properties.dealstage, count: length,
total: ([.[].properties.amount | select(. != null) | tonumber] | add // 0 | round)})
| sort_by(-.total) | .[] | "\(.stage)\tcount: \(.count)\tvalue: $\(.total)"' \
| column -t -s$'\t'hubspot objects search --type deals --filter "hs_is_closed!=true" \
--properties amount,hubspot_owner_id \
| jq -rs '
group_by(.properties.hubspot_owner_id)
| map({owner: .[0].properties.hubspot_owner_id, count: length,
total: ([.[].properties.amount | select(. != null) | tonumber] | add // 0 | round)})
| sort_by(-.total) | .[] | "owner \(.owner)\tdeals: \(.count)\tvalue: $\(.total)"' \
| column -t -s$'\t'hubspot owners list | jq -r '"\(.id)\t\(.firstName) \(.lastName) <\(.email)>"' > /tmp/owners.tsvhubspot objects search --type deals \
--filter "hs_is_closed_won=true AND closedate>=2026-04-01 AND closedate<2026-07-01" \
--properties dealname,amount,closedate,hubspot_owner_id
hubspot objects search --type deals \
--filter "hs_is_closed=true AND hs_is_closed_won!=true AND closedate>=2026-04-01 AND closedate<2026-07-01" \
--properties dealname,amount,closedate,hubspot_owner_idhubspot objects search --type deals \
--filter "hs_is_closed=true AND closedate>=2026-01-01" \
--properties hubspot_owner_id,hs_is_closed_won,amount \
| jq -rs '
group_by(.properties.hubspot_owner_id)
| map({owner: .[0].properties.hubspot_owner_id,
total: length,
won: ([.[] | select(.properties.hs_is_closed_won == "true")] | length),
won_value: ([.[] | select(.properties.hs_is_closed_won == "true")
| .properties.amount | select(. != null) | tonumber] | add // 0 | round)})
| map(. + {win_rate: ((.won / .total * 100) | round)})
| sort_by(-.won_value)
| .[] | "owner \(.owner)\twon: \(.won)/\(.total)\trate: \(.win_rate)%\twon: $\(.won_value)"' \
| column -t -s$'\t'hubspot objects search --type deals \
--filter "hs_is_closed_won=true AND closedate>=2026-01-01" \
--properties amount,closedate \
| jq -rs '
group_by(.properties.closedate[0:7])
| map({month: .[0].properties.closedate[0:7], count: length,
revenue: ([.[].properties.amount | select(. != null) | tonumber] | add // 0 | round)})
| sort_by(.month) | .[] | "\(.month)\tdeals: \(.count)\trevenue: $\(.revenue)"' \
| column -t -s$'\t'