mcpField
B
bulk-operations

bulk-operations

github.com/hubspot数据与数据库281.8k 次安装今天更新
代码仓库

关于 bulk-operations

bulk-operations 是 HubSpot 命令行工具的数据批量操作技能,面向需要读取、整理并写入大量 CRM 记录的任务。它说明 JSONL 管道、分页和数据重塑流程,并要求不可逆操作先预览,再通过摘要和确认执行。

主要功能

  1. 01以 JSONL 管道处理批量读写
  2. 02使用脚本分页收集完整结果
  3. 03通过 jq 重塑读写数据
  4. 04不可逆操作采用预览与摘要确认
  5. 05查看操作历史并检查失败结果

适用场景

  1. 01批量更新或删除筛选出的 CRM 记录
  2. 02将外部 JSONL 数据批量新增或更新
  3. 03分页导出记录后生成写入载荷
  4. 04核查批量操作记录及失败行

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/hubspot/agent-cli-skills --skill bulk-operations

安装后在 Claude Code 中输入/bulk-operations确认已加载。

附带文件resources/json-patterns.mdresources/pagination-loop.sh

详细介绍

资源

文件适用场景
resources/json-patterns.md转换模式:将读取结果转换为更新载荷、将搜索结果转换为删除列表、将 CSV 转换为 upsert 流。

信息来源

这是 hubspot Agent 命令行工具;开发者 CLI hs(@hubspot/cli)是另一种工具,不管理 CRM 数据或工作流。hubspot <command> --help 是权威依据。如果本文档中的任何内容与 --help 矛盾,请以 --help 为准,并告知用户。每次会话开始时运行一次 hubspot objects types,查看此门户中有哪些对象类型(标准类型 + 自定义类型)。

提交反馈

使用 hubspot feedback 命令向此 CLI 工具的所有者发送消息。传入 --source agent,以便将反馈归因于 Agent 流量(默认值为 user):

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

反馈内容可以是:

  • 遇到的具体错误和问题
  • 使用 CLI 前希望了解的信息
  • 用户感到困惑、沮丧或不满的内容
  • 用户提出但你无法完成的请求
  • 希望提供的工具、功能或技能,以便让今后的任务更轻松

该命令只需一行简短内容,会关联到当前 HubSpot 账户,也不会阻塞任务——发送后继续操作。

输出格式

每个读取命令(list、search、get)都会输出 JSONL,每行一个 JSON 对象:

{"id":"123","properties":{"email":"[email protected]","firstname":"Jane"},"createdAt":"...","updatedAt":"...","archived":false,"url":"..."}

--properties email,firstname 会限制服务在 .properties 下返回的字段。下游 jq 应使用 .properties.email,而不是 .prop_email。

写入命令(create、update、upsert、delete、merge、associations create)接受 stdin 中的 JSONL,并输出 JSONL,每行输入对应一个结果:{"id":"123","ok":true,"data":{...}} 或 {"id":"123","ok":false,"error":{"status":...,"message":"..."}}。结果顺序与输入顺序一致——可以安全地按行位置关联。

批量读取——切勿逐条读取

CLI 原生支持多个 ID。切勿将 ID 管道传给 xargs -I{} hubspot objects get ...——这会为每条记录启动一个 CLI 进程。

# 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

单次 hubspot objects get 调用通过批量端点最多可读取约 100 个 ID。超过此数量时,每次按 100 个分批读取。

批量处理流程:先分页,再转换,然后写入

处理某类对象的所有记录(或某个筛选条件匹配的所有记录)时,始终从 pagination-loop.sh 开始——切勿直接运行 list 或 search 来“查看有多少条记录”。直接调用最多返回 100 条记录,而你之后仍得重新获取这些记录。若要先估算任务规模,请使用 hubspot objects count --type <t> [--filter "..."];该命令会返回匹配记录总数(例如 {"object_type":"contacts","total":42}),不会进行分页。

标准批量处理模式如下:

  1. 分页获取所有记录并保存到 JSONL 文件
  2. 使用 jq 转换为写入载荷
  3. 管道传入写入命令(update、delete 等),并先使用 --dry-run

分页

list 和 search 每次调用最多返回 100 条记录。使用 resources/pagination-loop.sh 将所有页面收集到一个 JSONL 文件中:

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

示例:

# 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

该脚本会自动通过 --after 游标翻页,在 stderr 中显示进度,并将 JSONL 写入输出文件。请将其作为单个前台命令运行——不要在后台运行,也不要在内联脚本中重建循环。

结果排序

search 默认按最新记录优先排序——它会根据对象类型定义的创建日期属性按降序排序(联系人使用 createdate,活动使用 hs_createdate,依此类推)——因此,第一页包含的是最近的记录,而不是门户中 2017 年左右的记录。使用 --sort <property> 按其他可排序属性排序(hs_lastmodifieddate、amount 等),并使用 --sort-dir asc 切换排序方向。list 不支持排序控制——需要关注顺序时,请使用 search。

associations list 也支持分页(--limit / --after,默认限制为 100):在 --format json 下,下一页游标位于 .meta.next;到达最后一页后,该值为 null。一个记录关联的记录数量可能远超单页容量,因此只要游标存在,就表示“还有更多记录”,请使用 --after 翻页,直到游标为 null。

批量写入——始终通过管道输入

写入命令接受来自标准输入的 JSONL。读取格式与写入格式之间的转换通过 jq 重塑完成:

写入命令每行所需格式
objects create{"properties":{"field":"value"}}
objects update{"id":"123","properties":{"field":"value"}}
objects upsert{"idProperty":"email","id":"[email protected]","properties":{...}}(或使用一次 --id-property email)
objects delete{"id":"123"}
objects merge{"primary":"123","secondary":"456"}
associations create{"from":"contacts:123","to":"companies:456"}

from/to 中使用复数对象名称(contacts:,而不是 contact:)。

安全的破坏性操作工作流

不可逆写入——objects delete、merge、update、upsert、associations delete / limits-update,以及元数据删除(工作流、细分、视图、报告、架构、管道、属性)——始终需要经过两步的 dry-run → digest → confirm 流程,不论行数多少(删除 1 条记录和删除 10k 条记录同样需要)。软写入(objects create、views create/update/replace-field、仅更新标签的 properties update、segments members-add)无需 digest;其 --dry-run 只是普通预览。

1. Dry-run — 每次调用只输出一行预览(而非每条记录一行),无论数据量大小:

{"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 在 ≤100 行时为 RecordMutation,超过批量阈值时为 BulkData——两种情况下都会提供 digest,因此绝不要按 mutation_kind 过滤。确认值因命令而异:objects delete/update → 记录 ID(单条)或行数(批量 2 条及以上);objects upsert → 行数(始终如此);objects merge → 次要 ID(单对)或行数(批量);元数据删除 → 目标名称(工作流/视图/报告/架构/管道)、属性/选项名称,或行数(批量归档、关联批量操作)。不要自行构造确认值——请从预览行的 apply_command_hint(或 .target.id / .target.name)中复制。

2. 执行 在 5 分钟内完成(digest TTL 为 300s):重新通过管道传入相同的输入,并附上 --digest 和 --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"

未使用 --digest 执行会失败并返回 digest_required(错误中的 next_step 会指出 dry-run 命令);确认值错误会返回 confirm_mismatch;digest 过期会返回 digest_expired。--force 已弃用且会被忽略。

通过 hubspot history 恢复

每次破坏性操作(及其试运行)都会在本地记录。查看过去一小时发生了什么,以及哪些操作可以撤销:

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

history 目前不会恢复记录——它是审计日志。如果误删了内容,请截取相应的历史记录行,并告知用户通过 UI 恢复。

如需查看 CRM 属性的 来源 历史记录(谁或什么更改了属性——WORKFLOW、INTEGRATION、IMPORT、CRM_UI),请使用 hubspot objects history --type <t> --properties <p>。此命令会将每个属性的版本历史记录展开为每次更改一行;默认排除由 UI 驱动的更改(CRM_UI),使用 --include-ui 可保留这些更改。添加 --id <recordId> 可读取单条记录;省略该参数则扫描一个页面。这与上文的本地 hubspot history 审计日志不同——在批量操作后调查属性为何发生更改时,请使用此命令。

Upsert 优于先搜索再创建

对于“缺失时创建,存在时更新”(即数据补全模式),请使用 upsert——每条记录只需调用一次 CLI,不会发生竞态条件:

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"

速率限制注意事项

create、update 和 upsert 会将标准输入分批,每批最多 100 条,并调用实际的 batch/create / batch/update / batch/upsert 端点——每 100 行调用一次 API,而不是每行调用一次。delete 则为标准输入中的每一行调用一次 API。将 5 万行文件通过管道传入之前,先用 head -n 50 测试;或者使用专为批量导入设计的 hubspot imports。如果 API 开始返回 429 错误,每行(或每批)的输出都会显示 {"ok":false,"error":{"status":429,...}}——请拆分输入文件,并重试失败的行。

对于大型 CSV 导入,专为此设计的方案是 hubspot imports:imports start(使用 CSV 文件和 import-request JSON;支持 --dry-run)、imports list、imports get <id>、imports cancel <id>(不可撤销——先试运行,再使用导入 ID 执行 --digest/--confirm),以及 imports errors <id>。运行 hubspot imports --help 查看请求格式。

常见重塑方式

完整列表请参阅 resources/json-patterns.md。你 90% 的情况下只需要下面两种:

# 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"

已知限制

  • objects delete 支持使用用户 OAuth(浏览器登录,且具有对象的写入权限范围)和服务密钥;403 表示当前令牌缺少该写入权限范围。例外是 --gdpr 永久清除,该操作需要服务密钥(HUBSPOT_ACCESS_TOKEN)——GDPR 端点不接受用户 OAuth 令牌。objects update/merge/upsert 也接受这两种令牌类型。某些破坏性操作(例如 associations 的 create/delete/batch/labels/limits、schemas delete)仍仅支持服务密钥,并由服务端强制执行,因此 HUBSPOT_SKIP_AUTH_CHECK 无法让用户令牌绕过限制。
  • hubspot owners list 返回 CRM 用户;没有 teams 对象。对于团队级操作,请在客户端按 hubspot_owner_id 分组。
  • hubspot segments 提供 CRM 列表(Lists API):list、get、create、update(元数据)、update-filters、delete、restore,以及 members-list / members-add / members-remove。
  • hubspot sequences 提供对 Sales Hub 序列的只读访问:list --user-id <id>(分页,支持 --name 筛选)、get <id> --user-id <id>(步骤和设置),以及 enrollments <contact_id>(联系人的注册历史)。序列属于产品 API 范围(Sales Hub Professional+,automation.sequences.read 权限范围),而不是 CRM 对象类型——objects list --type sequences 不起作用,也没有 create/update/delete/enroll 操作。
  • **维护本节:**命令范围会不断扩展——不要因为编写本文时某个 API 不存在,就认定它现在也不存在。发布新的命令系列时(参见 CHANGELOG.md),请重新检查这些限制。hubspot --help 是当前可用功能的权威信息来源。

评论

支持 Markdown