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 從公開儲存庫中收錄這些內容。

檢舉或申請下架