bulk-operations

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Resources

资源

FileWhen to use
resources/json-patterns.md
Reshape patterns for turning a read into an update payload, a search into a delete list, a CSV into an upsert stream.
文件使用场景
resources/json-patterns.md
用于将读取结果转换为更新负载、搜索结果转换为删除列表、CSV转换为新增/更新流的格式转换模式。

Source of truth

权威参考

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).
hubspot <command> --help
是权威依据。如果本文档中的内容与
--help
输出矛盾,请以
--help
为准,并告知用户。在会话开始时运行
hubspot objects types
,查看此门户中存在的对象类型(标准+自定义)。

Output shape

输出格式

Every read command (
list
,
search
,
get
) emits JSONL — one JSON object per line:
json
{"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.
所有读取命令(
list
search
get
)都会输出JSONL——每行一个JSON对象:
json
{"id":"123","properties":{"email":"jane@example.com","firstname":"Jane"},"createdAt":"...","updatedAt":"...","archived":false,"url":"..."}
--properties email,firstname
参数限制服务器在
.properties
下返回的字段。下游的
jq
工具应使用
.properties.email
,而非
.prop_email
写入命令(
create
update
upsert
delete
merge
associations create
)从标准输入接收JSONL,并输出JSONL——每行输入对应一行结果:
{"id":"123","ok":true,"data":{...}}
{"id":"123","ok":false,"error":{"status":...,"message":"..."}}
。结果顺序与输入顺序一致。

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.
bash
undefined
CLI原生支持多个ID。切勿将ID通过管道传递给
xargs -I{} hubspot objects get ...
——这会为每条记录启动一个CLI进程。
bash
undefined

Positional args (small, known list)

位置参数(少量、已知列表)

hubspot objects get --type contacts 12345 67890 23456 --properties email,firstname
hubspot objects get --type contacts 12345 67890 23456 --properties email,firstname

Stdin from another command — one CLI call total

从其他命令获取标准输入——仅一次CLI调用

hubspot associations list --from companies:67890 --to contacts
| jq -c '{id}'
| hubspot objects get --type contacts --properties email,firstname,jobtitle
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

标准输入中的纯ID也可使用

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.
printf '12345\n67890\n23456\n' | hubspot objects get --type contacts --properties email

单次`hubspot objects get`调用通过批量端点最多读取约100个ID。如需处理更多ID,可按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.
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-loop.sh
开始
——切勿直接运行
list
search
来"查看记录数量"。直接调用最多返回100条记录,之后你仍需重新获取完整数据。
标准的批量操作模式为:
  1. 分页获取所有记录并保存到JSONL文件
  2. 使用
    jq
    转换格式
    为写入负载
  3. 通过管道传递到写入命令(
    update
    delete
    等),并先使用
    --dry-run
    预执行

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
bash resources/pagination-loop.sh <object_type> <output_file> [properties] [extra_flags...]
Examples:
bash
undefined
list
search
命令每次调用最多返回100条记录。使用
resources/pagination-loop.sh
脚本将所有页面收集到单个JSONL文件中:
bash
bash resources/pagination-loop.sh <object_type> <output_file> [properties] [extra_flags...]
示例:
bash
undefined

All contacts with specific properties

获取包含指定属性的所有联系人

bash resources/pagination-loop.sh contacts /tmp/contacts.jsonl email,firstname,lastname
bash resources/pagination-loop.sh contacts /tmp/contacts.jsonl email,firstname,lastname

Search with a filter (passes extra flags through to the CLI)

带筛选条件的搜索(将额外参数传递给CLI)

bash resources/pagination-loop.sh contacts /tmp/leads.jsonl email,firstname '--filter' 'lifecyclestage=lead'
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.
bash resources/pagination-loop.sh deals /tmp/deals.jsonl

该脚本会自动通过`--after`游标进行分页,将进度信息输出到标准错误流,并将JSONL写入输出文件。请将其作为单个前台命令运行——不要后台运行或手动重构循环逻辑。

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:
).
写入命令从标准输入接收JSONL。读取格式到写入格式的转换可通过
jq
完成:
写入命令每行所需格式
objects create
{"properties":{"field":"value"}}
objects update
{"id":"123","properties":{"field":"value"}}
objects upsert
{"idProperty":"email","id":"jane@example.com","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:
)。

Safe destructive workflow

安全破坏性工作流

Every destructive op (
delete
,
merge
, bulk
update
) supports
--dry-run
. The gating depends on row count:
≤100 rows — dry-run emits one preview line per record:
json
{"ok":true,"dry_run":true,"executed":false,"mutation_kind":"RecordMutation","command":"objects delete contacts","target":{"kind":"contacts_record","id":"123","name":"123"}}
Re-run without
--dry-run
to execute.
>100 rows — dry-run emits a single
BulkData
line with a digest and an
apply_command_hint
:
json
{"ok":true,"dry_run":true,"executed":false,"mutation_kind":"BulkData","portal":"123456","target":{"name":"202 records"},"impact":{"records_affected":202,"reversible":false},"digest":"blast-29cfdd48b583","expires_in_seconds":300,"apply_command_hint":"hubspot objects delete contacts --digest blast-29cfdd48b583 --confirm '202'"}
You must re-run with
--digest <hash> --confirm <value>
within 5 minutes. The
confirm
value is the record count (deletes) or the secondary ID (merge). Read it off
apply_command_hint
.
Three-step pattern:
bash
undefined
所有破坏性操作(
delete
merge
、批量
update
)均支持
--dry-run
参数。验证机制取决于记录数量:
≤100条记录——预执行会为每条记录输出一行预览信息:
json
{"ok":true,"dry_run":true,"executed":false,"mutation_kind":"RecordMutation","command":"objects delete contacts","target":{"kind":"contacts_record","id":"123","name":"123"}}
移除
--dry-run
参数重新运行即可执行操作。
>100条记录——预执行会输出一行
BulkData
信息,包含摘要和
apply_command_hint
json
{"ok":true,"dry_run":true,"executed":false,"mutation_kind":"BulkData","portal":"123456","target":{"name":"202 records"},"impact":{"records_affected":202,"reversible":false},"digest":"blast-29cfdd48b583","expires_in_seconds":300,"apply_command_hint":"hubspot objects delete contacts --digest blast-29cfdd48b583 --confirm '202'"}
你必须在5分钟内使用
--digest <hash> --confirm <value>
参数重新运行命令。
confirm
的值为记录数量(删除操作)或次要ID(合并操作),可从
apply_command_hint
中获取。
三步操作模式:
bash
undefined

1. Preview

1. 预览

hubspot objects search --type contacts --filter "lifecyclestage=subscriber"
| jq -c '{id}'
| hubspot objects delete --type contacts --dry-run
| tee /tmp/preview.jsonl
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 (only present for >100 rows)

2. 提取摘要和确认值(仅当记录数>100时存在)

digest=$(jq -r 'select(.mutation_kind=="BulkData") | .digest' /tmp/preview.jsonl) confirm=$(jq -r 'select(.mutation_kind=="BulkData") | .impact.records_affected' /tmp/preview.jsonl)
digest=$(jq -r 'select(.mutation_kind=="BulkData") | .digest' /tmp/preview.jsonl) confirm=$(jq -r 'select(.mutation_kind=="BulkData") | .impact.records_affected' /tmp/preview.jsonl)

3. Execute — re-pipe the SAME inputs

3. 执行——传递相同的输入

hubspot objects search --type contacts --filter "lifecyclestage=subscriber"
| jq -c '{id}'
| hubspot objects delete --type contacts --digest "$digest" --confirm "$confirm"
undefined
hubspot objects search --type contacts --filter "lifecyclestage=subscriber"
| jq -c '{id}'
| hubspot objects delete --type contacts --digest "$digest" --confirm "$confirm"
undefined

Recovery via
hubspot history

通过
hubspot history
进行恢复

Every destructive op (and its dry-run) is logged locally. Check what happened in the last hour and what's reversible:
bash
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.
所有破坏性操作(及其预执行)都会在本地记录。查看过去一小时内的操作记录以及可恢复的内容:
bash
hubspot history --since 1h --format table
hubspot history --since 24h --kind BulkData       # 仅查看批量操作
hubspot history --since 7d --kind MetadataDestroy # 仅查看架构删除操作
history
命令目前不支持恢复记录——它只是一个审计日志。如果误删了内容,请截取历史记录行,并告知用户通过UI进行恢复。

Upsert beats search-then-create

Upsert优于搜索后创建

For "create if missing, update if present" (the enrichment pattern), use
upsert
— one CLI call per record, no race condition:
bash
cat external.jsonl \
| jq -c '{idProperty:"email", id:.email, properties:{firstname:.first, lastname:.last, company:.company}}' \
| hubspot objects upsert --type contacts --dry-run
对于"不存在则创建,存在则更新"(数据补全模式),请使用
upsert
——每条记录仅需一次CLI调用,无竞态条件:
bash
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:

或一次性设置idProperty参数:

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

Rate-limit hygiene

限流规范

There is no true batch endpoint behind
update
/
delete
/
upsert
— the CLI issues one API call per stdin line. Test with
head -n 50
before piping a 50k-row file. If the API starts 429ing, the per-line output will show
{"ok":false,"error":{"status":429,...}}
— split your input file and retry the failed lines.
update
/
delete
/
upsert
命令背后没有真正的批量端点——CLI会为标准输入中的每一行发起一次API调用。在传递5万行的文件之前,请先用
head -n 50
进行测试。如果API返回429错误,每行输出会显示
{"ok":false,"error":{"status":429,...}}
——请拆分输入文件并重试失败的行。

Common reshapes

常见格式转换

See
resources/json-patterns.md
for the full set. The two you need 90% of the time:
bash
undefined
完整的转换模式请查看
resources/json-patterns.md
。90%的场景中你会用到以下两种:
bash
undefined

Read → update payload

读取结果 → 更新负载

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

Search → delete list

搜索结果 → 删除列表

hubspot objects search --type contacts --filter "!email"
| jq -c '{id}'
| hubspot objects delete --type contacts --dry-run
undefined
hubspot objects search --type contacts --filter "!email"
| jq -c '{id}'
| hubspot objects delete --type contacts --dry-run
undefined

Known constraints

已知限制

  • Some destructive operations may be blocked under user-OAuth (browser login); set
    HUBSPOT_ACCESS_TOKEN
    (private app token) when running deletes if the CLI returns a permission error.
  • hubspot owners list
    returns CRM users; there is no
    teams
    object. For team-level operations, group by
    hubspot_owner_id
    client-side.
  • No Lists API, no sequences/cadences API in the current CLI surface.
  • 在用户OAuth(浏览器登录)模式下,部分破坏性操作可能被阻止;如果CLI返回权限错误,请在运行删除操作时设置
    HUBSPOT_ACCESS_TOKEN
    (私有应用令牌)。
  • hubspot owners list
    返回CRM用户;目前没有
    teams
    对象。如需进行团队级操作,请在客户端按
    hubspot_owner_id
    分组。
  • 当前CLI界面中没有Lists API和序列/节奏(sequences/cadences)API。