CCM
/Skills
SkillsMCPMarketplacesDigestToolsAdvertise

This week in Claude

Every Monday: Claude Code, Agent SDK, MCP, and the Anthropic platform moves worth your time.

Skills by Category
Frontend DevelopmentBackend & APIsTesting & QASecurityDevOps & CI/CDGit & Pull RequestsDocumentationCode Review & QualityAI & Agent BuildingSkill Development
MCP Servers by Category
Sales & MarketingWeb & Browser AutomationDatabasesAI & LLM ToolsCloud & InfrastructureCommunication & MessagingDeveloper ToolsDesign & CreativeDocuments & KnowledgeSearch & Web Crawling
Marketplaces by Category
AI Agents & OrchestrationLLM IntegrationDevelopment ToolsFrontend & UIBackend & APIsDatabasesTesting & Code QualityDevOps & CloudSecurity & ComplianceGit & Version Control

Claude Code Marketplaces

Discover Claude Code plugins, extensions, and tools. Automatically updated directory of Anthropic Claude AI marketplaces with development tools, productivity plugins, and integrations.

Resources

  • Browse Skills
  • Browse MCP Servers
  • Browse Marketplaces
  • Skill index
  • MCP index
  • Marketplace index
  • Plugins Reference

Community

  • About
  • Tools
  • Feedback
  • Privacy Policy
  • Advertise

Built for the Claude Code community with Claude Code by mertbuilds.com

Independent project, not affiliated with Anthropic
hubspot avatar

Bulk Operations

hubspot/agent-cli-skills
1.1k installs18 stars
Summary

The foundation for everything else in the HubSpot CLI ecosystem. This gives you the patterns for moving data in and out at scale: JSONL pipes, batch reads that never hit the API one record at a time, pagination loops that collect all pages into a single file, and the dry-run/digest/confirm workflow for anything destructive. If you're updating 5,000 contacts or deleting a filtered list, you'll use these patterns. The history command acts as your audit log when something goes wrong. Every other HubSpot skill assumes you understand these mechanics, so start here before reaching for the specialized tools.

Install to Claude Code

npx -y skills add hubspot/agent-cli-skills --skill bulk-operations --agent claude-code

Installs into .claude/skills of the current project.

CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Lid closed, agents working
Lid closed, agents working
Keeps your Mac awake while Claude Code, Codex or Cursor works. Lets it sleep when they are done.
Try free for 7 days →
Block distraction from your iPhone for freeBlock distraction from your iPhone for free
Block distraction from your iPhone for free
Block distracting apps from your iPhone permanently without a 3rd party app. Free and open source.
Block now (100% free) →
Granola, the best AI meeting recorder
Granola, the best AI meeting recorder
Notes, actions and memory. Without a meeting bot. First month 100% off.
Download for free →
belt - the only tool your agent needs
belt - the only tool your agent needs
belt cli automatically finds the best tools and skills for your agent. image, video, music, tts...
one prompt install →
Open Steps
Open Steps
Free an open-source skills that make AI coding agents easier to understand, verify, and control.
Download for free →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Agent, connect blockchain
Agent, connect blockchain
Connect your Claude agent to live crypto prices and trading routes via 1inch
Get the MCP →
CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Lid closed, agents working
Lid closed, agents working
Keeps your Mac awake while Claude Code, Codex or Cursor works. Lets it sleep when they are done.
Try free for 7 days →
Block distraction from your iPhone for freeBlock distraction from your iPhone for free
Block distraction from your iPhone for free
Block distracting apps from your iPhone permanently without a 3rd party app. Free and open source.
Block now (100% free) →
Granola, the best AI meeting recorder
Granola, the best AI meeting recorder
Notes, actions and memory. Without a meeting bot. First month 100% off.
Download for free →
belt - the only tool your agent needs
belt - the only tool your agent needs
belt cli automatically finds the best tools and skills for your agent. image, video, music, tts...
one prompt install →
Open Steps
Open Steps
Free an open-source skills that make AI coding agents easier to understand, verify, and control.
Download for free →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Agent, connect blockchain
Agent, connect blockchain
Connect your Claude agent to live crypto prices and trading routes via 1inch
Get the MCP →
Files
SKILL.md

Resources

FileWhen to use
resources/json-patterns.mdReshape patterns for turning a read into an update payload, a search into a delete list, a CSV into an upsert stream.

Source of truth

This is the hubspot agent CLI; the hs developer CLI (@hubspot/cli) is a different tool and does not manage CRM data or workflows. hubspot <command> --help is authoritative. If anything in this file contradicts --help, trust --help and tell the user. Run hubspot objects types once at the start of a session to see what object types exist in this portal (standard + custom).

Submit Feedback

Use the hubspot feedback command to send a message to the owners of this CLI tool. Pass --source agent so it's attributed to agent traffic (it defaults to user):

hubspot feedback "batch upsert timed out on 5k rows" --source agent

This can be anything from:

  • Specific bugs and hiccups you encountered
  • Things you wish you knew before using the CLI
  • Anything your user got confused, frustrated, or upset about
  • Anything the user asked for that you couldn't do
  • Any tools, capabilities, or skills you wish existed that would make future tasks easier

It takes one short line, attaches to the active HubSpot account, and doesn't block the task — send it and keep going.

Output shape

Every read command (list, search, get) emits JSONL — one JSON object per line:

{"id":"123","properties":{"email":"jane@example.com","firstname":"Jane"},"createdAt":"...","updatedAt":"...","archived":false,"url":"..."}

--properties email,firstname limits which fields the server returns under .properties. Downstream jq should use .properties.email, not .prop_email.

Write commands (create, update, upsert, delete, merge, associations create) accept JSONL on stdin and emit JSONL — one result per input line: {"id":"123","ok":true,"data":{...}} or {"id":"123","ok":false,"error":{"status":...,"message":"..."}}. Order of results matches input order — safe to join by line position.

Read in batch — never one-by-one

The CLI accepts multiple IDs natively. Never pipe IDs into xargs -I{} hubspot objects get ... — that spawns one CLI process per record.

# Positional args (small, known list)
hubspot objects get --type contacts 12345 67890 23456 --properties email,firstname

# Stdin from another command — one CLI call total
hubspot associations list --from companies:67890 --to contacts \
| jq -c '{id}' \
| hubspot objects get --type contacts --properties email,firstname,jobtitle

# Bare IDs on stdin also work
printf '12345\n67890\n23456\n' | hubspot objects get --type contacts --properties email

A single hubspot objects get reads up to ~100 IDs per call via the batch endpoint. For more, page in chunks of 100.

Bulk flow: paginate first, then reshape, then write

When operating on all records of a type (or all matches of a filter), always start with pagination-loop.sh — never run a bare list or search to "check how many there are." A bare call returns at most 100 records and you will have to re-fetch them anyway. To size the job first, use hubspot objects count --type <t> [--filter "..."], which returns the total matching count (e.g. {"object_type":"contacts","total":42}) without paging.

The canonical bulk pattern is:

  1. Paginate all records to a JSONL file
  2. Reshape with jq into the write payload
  3. Pipe to the write command (update, delete, etc.) with --dry-run first

Pagination

list and search return at most 100 records per call. Use resources/pagination-loop.sh to collect all pages into a single JSONL file:

bash resources/pagination-loop.sh <object_type> <output_file> [properties] [extra_flags...]

Examples:

# All contacts with specific properties
bash resources/pagination-loop.sh contacts /tmp/contacts.jsonl email,firstname,lastname

# Search with a filter (passes extra flags through to the CLI)
bash resources/pagination-loop.sh contacts /tmp/leads.jsonl email,firstname '--filter' 'lifecyclestage=lead'

# All deals, default properties
bash resources/pagination-loop.sh deals /tmp/deals.jsonl

The script pages through --after cursors automatically, prints progress to stderr, and writes JSONL to the output file. Run it as a single foreground command — do not background it or reconstruct the loop inline.

Result ordering

search returns newest-first by default — it sorts descending on whichever create-date property the object type defines (createdate for contacts, hs_createdate for activities, and so on) — so the first page holds the most recent records rather than the portal's 2017-era ones. Sort on any other sortable property with --sort <property> (hs_lastmodifieddate, amount, ...) and flip direction with --sort-dir asc. list has no sort control — use search when order matters.

associations list also paginates (--limit / --after, default limit 100): under --format json the next-page cursor is at .meta.next, null once the last page is reached. A record can have far more associated records than one page holds, so treat a present cursor as "more remain" and page with --after until it is null.

Write in batch — always pipe

Write commands accept JSONL on stdin. The transformation between a read shape and a write shape is a jq reshape:

Write commandRequired per-line shape
objects create{"properties":{"field":"value"}}
objects update{"id":"123","properties":{"field":"value"}}
objects upsert{"idProperty":"email","id":"jane@example.com","properties":{...}} (or use --id-property email once)
objects delete{"id":"123"}
objects merge{"primary":"123","secondary":"456"}
associations create{"from":"contacts:123","to":"companies:456"}

Use plural object names in from/to (contacts:, not contact:).

Safe destructive workflow

Irreversible writes — objects delete, merge, update, upsert, associations delete / limits-update, and metadata deletes (workflows, segments, views, reports, schemas, pipelines, properties) — always require the two-step dry-run → digest → confirm flow, at ANY row count (one record needs it just as much as 10k). Soft writes (objects create, views create/update/replace-field, label-only properties update, segments members-add) run without a digest; their --dry-run is a plain preview.

1. Dry-run — emits ONE preview line for the whole invocation (not per record), at every size:

{"ok":true,"dry_run":true,"executed":false,"mutation_kind":"RecordMutation","command":"objects delete --type contacts","target":{"kind":"contacts_record","id":"123","name":"123"},"impact":{"records_affected":1,"note":"Delete 1 contacts record(s)","reversible":false},"portal":"123456","digest":"blast-29cfdd48b583","expires_in_seconds":300,"apply_command_hint":"hubspot objects delete --type contacts --digest blast-29cfdd48b583 --confirm '123'"}

mutation_kind is RecordMutation for ≤100 rows and BulkData above the bulk threshold — the digest is present in both cases, so never filter on mutation_kind. Confirm values by command: objects delete/update → the record ID (single) or the row count (batch of 2+); objects upsert → the row count (always); objects merge → the secondary ID (one pair) or the row count (batch); metadata deletes → the target's name (workflow/view/report/schema/pipeline), the property/option name, or the row count (batch archives, associations batch ops). Don't construct the confirm value — copy it from apply_command_hint (or .target.id / .target.name on the preview line).

2. Execute within 5 minutes (digest TTL 300s): re-pipe the SAME inputs plus --digest and --confirm:

# 1. Preview
hubspot objects search --type contacts --filter "lifecyclestage=subscriber" \
| jq -c '{id}' \
| hubspot objects delete --type contacts --dry-run \
| tee /tmp/preview.jsonl

# 2. Lift the digest + confirm value (present at EVERY row count)
digest=$(jq -r 'select(.digest != null) | .digest' /tmp/preview.jsonl)
confirm=$(jq -r 'select(.digest != null) | .target.id' /tmp/preview.jsonl)

# 3. Execute — re-pipe the SAME inputs
hubspot objects search --type contacts --filter "lifecyclestage=subscriber" \
| jq -c '{id}' \
| hubspot objects delete --type contacts --digest "$digest" --confirm "$confirm"

Executing without --digest fails with digest_required (the error's next_step names the dry-run command); a wrong confirm fails with confirm_mismatch; an expired digest with digest_expired. --force is deprecated and ignored.

Recovery via hubspot history

Every destructive op (and its dry-run) is logged locally. Check what happened in the last hour and what's reversible:

hubspot history --since 1h --format table
hubspot history --since 24h --kind BulkData       # only bulk ops
hubspot history --since 7d --kind MetadataDestroy # schema deletes

history does not currently restore records — it's an audit log. If you deleted something by mistake, capture the history line and tell the user to restore via the UI.

For CRM property source history (who or what changed a property — WORKFLOW, INTEGRATION, IMPORT, CRM_UI), use hubspot objects history --type <t> --properties <p>. It flattens each property's version history into one row per change; UI-driven (CRM_UI) changes are excluded by default (--include-ui to keep them). Add --id <recordId> to read one record, or omit it to scan a page. This is separate from the local hubspot history audit log above — use it to investigate why a property changed after a bulk op.

Upsert beats search-then-create

For "create if missing, update if present" (the enrichment pattern), use upsert — one CLI call per record, no race condition:

cat external.jsonl \
| jq -c '{idProperty:"email", id:.email, properties:{firstname:.first, lastname:.last, company:.company}}' \
| hubspot objects upsert --type contacts --dry-run

# Or set idProperty once (upsert is irreversible — dry-run first, then execute):
cat external.jsonl \
| jq -c '{id:.email, properties:{firstname:.first}}' \
| hubspot objects upsert --type contacts --id-property email --dry-run \
| tee /tmp/upsert.preview.jsonl

digest=$(jq -r 'select(.digest != null) | .digest' /tmp/upsert.preview.jsonl)
confirm=$(jq -r 'select(.digest != null) | .target.id' /tmp/upsert.preview.jsonl)   # upsert confirm = the row count, even for one row

cat external.jsonl \
| jq -c '{id:.email, properties:{firstname:.first}}' \
| hubspot objects upsert --type contacts --id-property email --digest "$digest" --confirm "$confirm"

Rate-limit hygiene

create, update, and upsert batch stdin into chunks of up to 100 and call the real batch/create / batch/update / batch/upsert endpoints — one API call per 100 lines, not per line. delete issues one API call per stdin line. Test with head -n 50 before piping a 50k-row file — or use hubspot imports for purpose-built bulk ingest. If the API starts 429ing, the per-line (or per-chunk) output will show {"ok":false,"error":{"status":429,...}} — split your input file and retry the failed lines.

For large CSV ingests, hubspot imports is the purpose-built path: imports start (from a CSV file + import-request JSON; supports --dry-run), imports list, imports get <id>, imports cancel <id> (irreversible — dry-run first, then --digest/--confirm with the import ID), and imports errors <id>. Run hubspot imports --help for the request shape.

Common reshapes

See resources/json-patterns.md for the full set. The two you need 90% of the time:

# Read → update payload (update is irreversible — dry-run, then re-pipe with the digest/confirm from the preview)
hubspot objects search --type contacts --filter "industry=Tech" \
| jq -c '{id, properties:{lifecyclestage:"marketingqualifiedlead"}}' \
| hubspot objects update --type contacts --dry-run \
| tee /tmp/update.preview.jsonl

digest=$(jq -r 'select(.digest != null) | .digest' /tmp/update.preview.jsonl)
confirm=$(jq -r 'select(.digest != null) | .target.id' /tmp/update.preview.jsonl)   # single: record ID; batch of 2+: row count

hubspot objects search --type contacts --filter "industry=Tech" \
| jq -c '{id, properties:{lifecyclestage:"marketingqualifiedlead"}}' \
| hubspot objects update --type contacts --digest "$digest" --confirm "$confirm"

# Search → delete list (delete is irreversible — dry-run, lift, then re-pipe with --digest/--confirm)
hubspot objects search --type contacts --filter "!email" \
| jq -c '{id}' \
| hubspot objects delete --type contacts --dry-run \
| tee /tmp/delete.preview.jsonl

digest=$(jq -r 'select(.digest != null) | .digest' /tmp/delete.preview.jsonl)
confirm=$(jq -r 'select(.digest != null) | .target.id' /tmp/delete.preview.jsonl)   # single: record ID; batch of 2+: row count

hubspot objects search --type contacts --filter "!email" \
| jq -c '{id}' \
| hubspot objects delete --type contacts --digest "$digest" --confirm "$confirm"

Known constraints

  • objects delete works under both user-OAuth (browser login, with the object's write scope) and a service key; a 403 means the active token is missing that write scope. The exception is the --gdpr permanent purge, which requires a service key (HUBSPOT_ACCESS_TOKEN) — the GDPR endpoint does not accept user OAuth tokens. objects update/merge/upsert also accept either token type. Some destructive operations (e.g. associations create/delete/batch/labels/limits, schemas delete) remain service-key-only and are enforced server-side, so HUBSPOT_SKIP_AUTH_CHECK will not get a user token past them.
  • hubspot owners list returns CRM users; there is no teams object. For team-level operations, group by hubspot_owner_id client-side.
  • hubspot segments provides CRM lists (Lists API): list, get, create, update (metadata), update-filters, delete, restore, and members-list / members-add / members-remove.
  • hubspot sequences provides read-only access to Sales Hub sequences: list --user-id <id> (paginated, --name filter), get <id> --user-id <id> (steps + settings), and enrollments <contact_id> (a contact's enrollment history). Sequences are a product API surface (Sales Hub Professional+, automation.sequences.read scope), not a CRM object type — objects list --type sequences does not work, and there is no create/update/delete/enroll.
  • Maintaining this section: the command surface grows — do not assume an API is absent because it was when this was written. When a new command family ships (see CHANGELOG.md), revisit these constraints. hubspot --help is authoritative for what exists today.
Featured
CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Lid closed, agents working
Lid closed, agents working
Keeps your Mac awake while Claude Code, Codex or Cursor works. Lets it sleep when they are done.
Try free for 7 days →
Block distraction from your iPhone for freeBlock distraction from your iPhone for free
Block distraction from your iPhone for free
Block distracting apps from your iPhone permanently without a 3rd party app. Free and open source.
Block now (100% free) →
Granola, the best AI meeting recorder
Granola, the best AI meeting recorder
Notes, actions and memory. Without a meeting bot. First month 100% off.
Download for free →
belt - the only tool your agent needs
belt - the only tool your agent needs
belt cli automatically finds the best tools and skills for your agent. image, video, music, tts...
one prompt install →
Open Steps
Open Steps
Free an open-source skills that make AI coding agents easier to understand, verify, and control.
Download for free →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Agent, connect blockchain
Agent, connect blockchain
Connect your Claude agent to live crypto prices and trading routes via 1inch
Get the MCP →
Categories
AI & Agent BuildingAutomation & WorkflowsSales & MarketingCLI & Terminal
First SeenJul 14, 2026
View on GitHub

More from hubspot/agent-cli-skills

All 15 skills →
  • Crm Data Quality1.1k
  • Workflow Automation1.1k
  • Data Enrichment1k
  • Communication History1k
  • Custom Object Management1k
  • Deal Management1k
  • Sales Reporting1k
  • Sales Execution1k
  • Team Ownership1k
  • Customer Retention1k
  • Audience Targeting1k
  • Quote To Cash1k
  • Ticket Resolution1.3k
  • Crm Lookup1.1k

Recommended

More AI & Agent Building →
k-dense-ai avatar
deeptools

k-dense-ai/scientific-agent-skills

NGS analysis toolkit. BAM to bigWig conversion, QC (correlation, PCA, fingerprints), heatmaps/profiles (TSS, peaks), for ChIP-seq, RNA-seq, ATAC-seq visualization.
1.1k
33k
k-dense-ai avatar
research-lookup

k-dense-ai/scientific-agent-skills

Compile current scholarly evidence for a scientific manuscript or research brief. Use when the user explicitly asks to gather literature, references, background evidence, competing findings, or a manuscript research packet. Uses Parallel Search by default, Parallel Extract for source verification, Parallel Research for explicitly deep/exhaustive work, optional explicit Parallel Chat, and optional Perplexity only when requested or allowed as a failure fallback.
1.1k
33k
k-dense-ai avatar
scanpy

k-dense-ai/scientific-agent-skills

Standard single-cell RNA-seq analysis pipeline. Use for QC, normalization, dimensionality reduction (PCA/UMAP/t-SNE), clustering, differential expression, visualization, and converting R-friendly single-cell formats such as Seurat or SingleCellExperiment RDS files into h5ad for Scanpy. Best for exploratory scRNA-seq analysis with established workflows. For deep learning models use scvi-tools; for data format questions use anndata.
1.1k
33k
k-dense-ai avatar
benchling-integration

k-dense-ai/scientific-agent-skills

Benchling Python SDK and REST API integration for registry entities, inventory, ELN entries, workflows, Benchling Apps, and Data Warehouse queries. Use when automating lab data with benchling-sdk or the v2 API.
1.1k
33k
k-dense-ai avatar
pyhealth

k-dense-ai/scientific-agent-skills

Build clinical/healthcare deep-learning pipelines with PyHealth — loading EHR/signal/imaging datasets (MIMIC-III/IV, eICU, OMOP, SleepEDF, ChestXray14, EHRShot), defining tasks (mortality, readmission, length-of-stay, drug recommendation, sleep staging, ICD coding, EEG events), instantiating models (Transformer, RETAIN, GAMENet, SafeDrug, MICRON, StageNet, AdaCare, CNN/RNN/MLP), training with the PyHealth Trainer, computing clinical metrics, and using medical code utilities (ICD/ATC/NDC/RxNorm lookup and cross-mapping). Use this skill whenever the user mentions PyHealth, MIMIC, eICU, OMOP, EHR modeling, clinical prediction, drug recommendation, sleep staging, medical code mapping, ICD/ATC codes, or any healthcare ML pipeline that fits the dataset → task → model → trainer → metrics pattern, even if "PyHealth" isn't named explicitly.
1.1k
33k
k-dense-ai avatar
rdkit

k-dense-ai/scientific-agent-skills

Cheminformatics toolkit for fine-grained molecular control. SMILES/SDF parsing, descriptors (MW, LogP, TPSA), fingerprints, substructure search, 2D/3D generation, similarity, reactions. For standard workflows with simpler interface, use datamol (wrapper around RDKit). Use rdkit for advanced control, custom sanitization, specialized algorithms.
1.1k
33k