Deal Management

hubspot/agent-cli-skills/deal-management

作者 hubspota8eea0880838无许可证27 个星标收录于 2026年10月8日更新于 2026年10月8日仓库7天前更新

Run the full deal lifecycle from CLI — discover pipelines/stages, qualify MQLs into deals with associations, advance/reassign in bulk, hunt stalled deals, and close.

仅含说明Marketing & Sales
AI 生成的概览

通过命令行管理 HubSpot 交易全生命周期:发现管道、将 MQL 转化为交易、批量推进或改派、查找停滞交易并完成成交。

功能
该技能提供端到端管理 HubSpot 交易的命令行方法:发现门户专属的管道与阶段,将营销合格线索转化为交易并建立联系人与公司关联,以及推进生命周期阶段。它还涵盖批量阶段推进与负责人改派、查询停滞或已过成交日期的交易,以及通过更新阶段和成交日期完成成交。所有批量变更都遵循试运行、摘要与确认流程,并附带两个资源文件,分别说明阶段推进和停滞交易筛选条件。
适用场景
适用于通过命令行处理 HubSpot 交易的场景,例如将 MQL 转化为交易、批量推进或改派交易、查找停滞或逾期交易,或完成成交。前提是已具备 HubSpot CLI 及其批量操作约定。
运行要求
需要 HubSpot CLI(hubspot 命令)、jq,以及 paste、date 等 shell 工具;需要可访问包含交易、联系人、公司和负责人数据的 HubSpot 门户。该技能不附带脚本,只有说明文档和两个 markdown 资源文件,并引用配套技能(bulk-operations、sales-execution、sales-reporting)。

Resources

FileWhen to use
resources/lifecycle-stage-progression.mdLifecycle stage API values + the contact-side updates that pair with deal moves.
resources/stalled-deal-queries.mdFilter cookbook for stalled / no-activity / past-close-date deals with dynamic dates.

Foundations

Read bulk-operations/SKILL.md first — JSONL piping, batch read, pagination, and the dry-run/digest/confirm flow live there. Reshape recipes are in bulk-operations/resources/json-patterns.md. hubspot <command> --help is the source of truth. Object types are plural (contacts, deals, companies). For property reference: hubspot properties list --type deals — don't hardcode property tables.

1. Discover pipelines and stages

Pipeline and stage IDs are portal-specific. Always discover at runtime — never hardcode across portals.

bash
hubspot pipelines list --type deals --format jsonl# {"id":"default","label":"Sales Pipeline","displayOrder":0,"stages":[...]}# {"id":"a1b2c3d4-0000-0000-0000-000000000000","label":"Enterprise Pipeline","displayOrder":1,"stages":[...]}
hubspot pipelines stages --type deals --pipeline default --format jsonl# {"id":"appointmentscheduled","label":"Appointment Scheduled","displayOrder":0,"metadata":{"isClosed":"false","probability":"0.2"}}# {"id":"qualifiedtobuy","label":"Qualified To Buy","displayOrder":1,"metadata":{"isClosed":"false","probability":"0.4"}}# ...# {"id":"closedwon","label":"Closed Won","displayOrder":5,"metadata":{"isClosed":"true","probability":"1.0"}}# {"id":"closedlost","label":"Closed Lost","displayOrder":6,"metadata":{"isClosed":"true","probability":"0.0"}}

pipelines list/get embed each pipeline's stages, so a single call gives you the stage id -> label map you need to translate a deal's dealstage GUID:

bash
hubspot pipelines get --type deals default --format jsonl \  | jq -r '.stages[] | "\(.id)\t\(.label)"'

Grab a specific stage ID by label:

bash
QUALIFIED=$(hubspot pipelines stages --type deals --pipeline default --format jsonl \  | jq -r 'select(.label=="Qualified To Buy") | .id')

The IDs shown above (appointmentscheduled, closedwon, etc.) are HubSpot's standard default deal pipeline stages — but discover yours every run since portals can rename or remove them.

2. Qualify an MQL into a deal

Find connected MQLs without a deal, then for each: create the deal, associate to contact + company, promote lifecycle.

bash
# 1. find ready MQLshubspot objects search --type contacts \  --filter "lifecyclestage=marketingqualifiedlead AND hs_lead_status=CONNECTED AND num_associated_deals=0" \  --properties email,firstname,lastname,company,hubspot_owner_id
# 2. for one contact: company lookup, deal create, associate, promotehubspot associations list --from contacts:<contact_id> --to companies   # → <company_id>
hubspot objects create --type deals \  --property "dealname=Acme Corp - Inbound" \  --property pipeline=default --property dealstage=qualifiedtobuy \  --property amount=0 --property hubspot_owner_id=<owner_id># returns {"id":"<deal_id>","ok":true,...}
hubspot associations create --from deals:<deal_id> --to contacts:<contact_id>hubspot associations create --from deals:<deal_id> --to companies:<company_id>
# lifecycle promote — objects update is irreversible, so dry-run then confirm with the contact IDhubspot objects update --type contacts <contact_id> \  --property lifecyclestage=salesqualifiedlead --property hs_lead_status=OPEN_DEAL --dry-runhubspot objects update --type contacts <contact_id> \  --property lifecyclestage=salesqualifiedlead --property hs_lead_status=OPEN_DEAL \  --digest <hash> --confirm <contact_id>

Bulk pattern — many MQLs at once

objects create returns one result line per stdin line, in input order. Capture both streams and join by line for associations:

bash
# 1. snapshot MQLs to a file (preserves order for the join)hubspot objects search --type contacts \  --filter "lifecyclestage=marketingqualifiedlead AND hs_lead_status=CONNECTED AND num_associated_deals=0" \  --properties email,firstname,lastname,company,hubspot_owner_id \  > /tmp/mqls.jsonl
# 2. one deal per MQL — output preserves orderjq -c '{properties:{    dealname: ((.properties.firstname // "") + " " + (.properties.lastname // "") + " - " + (.properties.company // "Unknown")),    pipeline:"default", dealstage:"qualifiedtobuy", amount:"0", dealtype:"newbusiness",    hubspot_owner_id:(.properties.hubspot_owner_id // "")  }}' /tmp/mqls.jsonl \| hubspot objects create --type deals > /tmp/deals.jsonl
# 3. abort if any create failed — paste would zip null deal IDs onto real contactsjq -e 'select(.ok==false)' /tmp/deals.jsonl > /dev/null && { echo "Some deal creates failed — inspect /tmp/deals.jsonl" >&2; exit 1; }
# 4. pair contact <-> new deal by line for the association callpaste <(jq -r '.id' /tmp/mqls.jsonl) <(jq -r '.id' /tmp/deals.jsonl) \| jq -cR 'split("\t") | {from:("deals:" + .[1]), to:("contacts:" + .[0])}' \| hubspot associations create
# 5. promote lifecycle on every contact — objects update is irreversible, so dry-run then re-pipe with the digest/confirmjq -c '{id, properties:{lifecyclestage:"salesqualifiedlead", hs_lead_status:"OPEN_DEAL"}}' /tmp/mqls.jsonl \| hubspot objects update --type contacts --dry-run \| tee /tmp/promote.preview.jsonl
digest=$(jq -r 'select(.digest != null) | .digest' /tmp/promote.preview.jsonl)confirm=$(jq -r 'select(.digest != null) | .target.id' /tmp/promote.preview.jsonl)   # batch: row count
jq -c '{id, properties:{lifecyclestage:"salesqualifiedlead", hs_lead_status:"OPEN_DEAL"}}' /tmp/mqls.jsonl \| hubspot objects update --type contacts --digest "$digest" --confirm "$confirm"

Company associations need a separate per-contact pass via hubspot associations list --from contacts:<id> --to companies — a contact may have zero or many companies.

Pre-qualification checks are just filters on the search: has email, has a company, no open deal, has an owner — all in the --filter already. See resources/lifecycle-stage-progression.md for the full stage progression and contact-side updates.

3. Advance or reassign in bulk

bash
# move every deal in one stage to the next — 1. previewhubspot objects search --type deals --filter "dealstage=qualifiedtobuy" \| jq -c '{id, properties:{dealstage:"presentationscheduled"}}' \| hubspot objects update --type deals --dry-run \| tee /tmp/advance.preview.jsonl
# 2. lift the digest + confirm (present at every size)digest=$(jq -r 'select(.digest != null) | .digest' /tmp/advance.preview.jsonl)confirm=$(jq -r 'select(.digest != null) | .target.id' /tmp/advance.preview.jsonl)   # batch: row count; single: record ID
# 3. execute — re-pipe the SAME inputs plus --digest/--confirmhubspot objects search --type deals --filter "dealstage=qualifiedtobuy" \| jq -c '{id, properties:{dealstage:"presentationscheduled"}}' \| hubspot objects update --type deals --digest "$digest" --confirm "$confirm"
# reassign open deals from one rep to another — same three stepsOLD=$(hubspot owners list --format jsonl | jq -r 'select(.email=="[email protected]") | .id')NEW=$(hubspot owners list --format jsonl | jq -r 'select(.email=="[email protected]") | .id')hubspot objects search --type deals --filter "hubspot_owner_id=$OLD AND hs_is_closed!=true" \| jq -c "{id, properties:{hubspot_owner_id:\"$NEW\"}}" \| hubspot objects update --type deals --dry-run \| tee /tmp/deal-reassign.preview.jsonldigest=$(jq -r 'select(.digest != null) | .digest' /tmp/deal-reassign.preview.jsonl)confirm=$(jq -r 'select(.digest != null) | .target.id' /tmp/deal-reassign.preview.jsonl)hubspot objects search --type deals --filter "hubspot_owner_id=$OLD AND hs_is_closed!=true" \| jq -c "{id, properties:{hubspot_owner_id:\"$NEW\"}}" \| hubspot objects update --type deals --digest "$digest" --confirm "$confirm"

The dry-run emits a digest at every size (confirm = the row count for a batch, the record ID for a single); re-pipe with --digest <hash> --confirm <value> lifted from the preview line. Full flow in bulk-operations/SKILL.md.

4. Find stalled deals

Filter cookbook with dynamic dates lives in resources/stalled-deal-queries.md. The core query:

bash
# open deals with no activity in 30 days (macOS / Linux date examples in resources)hubspot objects search --type deals \  --filter "hs_last_activity_date<$(date -v-30d +%Y-%m-%d) AND hs_is_closed!=true" \  --properties dealname,dealstage,closedate,hubspot_owner_id,hs_last_activity_date

Pipe the result into an update (extend close dates, move stage, set a flag) or into task creation. For follow-up tasks/calls/notes against stalled deals, see the sales-execution skill — don't duplicate activity-object property handling here.

bash
# extend close dates for everything past duehubspot objects search --type deals \  --filter "closedate<$(date +%Y-%m-%d) AND hs_is_closed!=true" \| jq -c '{id, properties:{closedate:"2026-06-30"}}' \| hubspot objects update --type deals --dry-run

5. Close

Closing is a stage update + closedate (YYYY-MM-DD). hs_is_closed and hs_is_closed_won are read-only — HubSpot derives them from the stage.

bash
# single — objects update is irreversible, so dry-run then confirm with the deal IDhubspot objects update --type deals <deal_id> \  --property dealstage=closedwon --property closedate=2026-05-15 --dry-runhubspot objects update --type deals <deal_id> \  --property dealstage=closedwon --property closedate=2026-05-15 --digest <hash> --confirm <deal_id>
# bulk — preview first, then re-pipe with the digest/confirm from the preview (confirm = row count)hubspot objects search --type deals --filter "dealstage=contractsent AND hubspot_owner_id=<owner_id>" \| jq -c '{id, properties:{dealstage:"closedwon", closedate:"2026-05-15"}}' \| hubspot objects update --type deals --dry-run \| tee /tmp/close.preview.jsonl
digest=$(jq -r 'select(.digest != null) | .digest' /tmp/close.preview.jsonl)confirm=$(jq -r 'select(.digest != null) | .target.id' /tmp/close.preview.jsonl)
hubspot objects search --type deals --filter "dealstage=contractsent AND hubspot_owner_id=<owner_id>" \| jq -c '{id, properties:{dealstage:"closedwon", closedate:"2026-05-15"}}' \| hubspot objects update --type deals --digest "$digest" --confirm "$confirm"

Win/loss analysis (close reasons, win rate, ARR roll-up) is in the sales-reporting skill.

Known constraints

  • Bulk MQL → deal needs a two-pass shell flow: associations must be built from objects create output, not in the same pipe.
  • lifecyclestage is forward-only in most portal settings — backward transitions may be rejected.
  • closedate is a date string (YYYY-MM-DD). Datetime activity props (hs_last_activity_date) also accept a date string for </> comparisons.
  • hubspot sequences reads Sales Hub sequences (list / get / enrollments) but is read-only — the CLI cannot enroll a contact in a sequence, so create a follow-up task via sales-execution instead. sequences enrollments <contact_id> is useful for win/loss context on a deal's contacts.

来源与署名

来源:hubspot/agent-cli-skills位于deal-management提交a8eea08

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架