hubspot/agent-cli-skills

team-ownership

Assign and reassign CRM record ownership, audit who-owns-what across object types, and handle rep transitions. Built on `bulk-operations`.

All-time #8510 First seen May 26, 2026
8-week activity · all time api

Installation

$ npx skills add hubspot/agent-cli-skills --skill team-ownership

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from hubspot/agent-cli-skills · top by installs.

npx skills add hubspot/agent-cli-skills

Browse all from hubspot/agent-cli-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 22
License LICENSE
Default branch main
Open issues 1
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,269 B
  • docs SUMMARY.md 160 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1,300 installs

SKILL.md

Prereq: read bulk-operations/SKILL.md first. JSONL piping, pagination, dry-run/digest/confirm, and hubspot history recovery live there. Reshape patterns live in bulk-operations/resources/json-patterns.md.

hubspotownerid is a string field on contacts, companies, deals, and tickets. Owners are CRM users — hubspot owners list returns them; there is no teams object, so team-level views are client-side groupings by hubspotownerid.

1. Resolve email → owner ID

Never hardcode IDs — they are portal-specific. Resolve, then cache:

FROM_ID=$(hubspot owners list | jq -r 'select(.email=="[email protected]") | .id')
TO_ID=$(hubspot owners list | jq -r 'select(.email=="[email protected]")  | .id')

2. Find records for an owner

Same filter across all four object types. Add object-specific --properties for context. Unowned records use the !property form.

hubspot objects search --type contacts  --filter "hubspot_owner_id=$FROM_ID" --properties email,firstname,lifecyclestage
hubspot objects search --type companies --filter "hubspot_owner_id=$FROM_ID" --properties name,domain
hubspot objects search --type deals     --filter "hubspot_owner_id=$FROM_ID" --properties dealname,dealstage,amount
hubspot objects search --type tickets   --filter "hubspot_owner_id=$FROM_ID" --properties subject,hs_pipeline_stage

# Records with no owner at all
hubspot objects search --type deals --filter "!hubspot_owner_id" --properties dealname,amount

100 hits — page with the --after loop from bulk-operations. Counting only: pipe to wc -l.

3. Bulk reassign — search → update

Reshape each search row into {id, properties:{hubspotownerid}} and pipe to objects update. Always dry-run first; for >100 rows the dry-run emits a digest + applycommandhint — re-run with --digest/--confirm (see bulk-operations/SKILL.md § "Safe destructive workflow").

# Dry-run
hubspot objects search --type contacts --filter "hubspot_owner_id=$FROM_ID" \
| jq -c --arg to "$TO_ID" '{id, properties:{hubspot_owner_id:$to}}' \
| hubspot objects update --type contacts --dry-run

# Execute — ≤100: drop --dry-run.  >100: append --digest <hash> --confirm <count>.
hubspot objects search --type contacts --filter "hubspot_owner_id=$FROM_ID" \
| jq -c --arg to "$TO_ID" '{id, properties:{hubspot_owner_id:$to}}' \
| hubspot objects update --type contacts

Single-record assignment — no stdin, no jq:

hubspot objects update --type contacts 12345 --property hubspot_owner_id=$TO_ID

4. Rep-leaves workflow

Loop over the four object types the rep touches:

FROM_ID=$(hubspot owners list | jq -r 'select(.email=="[email protected]")    | .id')
TO_ID=$(hubspot  owners list | jq -r 'select(.email=="[email protected]") | .id')

for type in contacts companies deals tickets; do
  echo "── $type ──"
  hubspot objects search --type "$type" --filter "hubspot_owner_id=$FROM_ID" \
  | jq -c --arg to "$TO_ID" '{id, properties:{hubspot_owner_id:$to}}' \
  | hubspot objects update --type "$type" --dry-run
done

Review each digest line, then re-run without --dry-run (adding --digest/--confirm per type when escalated). Mis-reassigned? hubspot history --since 1h lists the affected IDs.

5. Team-level views (client-side grouping)

Group records by hubspotownerid, join to owners list for human-readable emails:

hubspot objects search --type deals --filter "dealstage!=closedwon AND dealstage!=closedlost" \
  --properties hubspot_owner_id --format json \
| jq '.data | group_by(.properties.hubspot_owner_id)
       | map({owner_id: .[0].properties.hubspot_owner_id, count: length})' \
> /tmp/by-owner.json

hubspot owners list \
| jq --slurpfile by /tmp/by-owner.json -r \
     '. as $o | $by[0][] | select(.owner_id==$o.id) | "\($o.email)\t\(.count)"'