data-enrichment — скилл для Claude Code: что делает и как установить
Скилл сопоставляет CSV или JSONL с контактами и компаниями в HubSpot и обновляет базу через upsert без лишних поисковых запросов.
npx skills add HubSpot/agent-cli-skills --skill data-enrichment -a claude-codeЧто делает data-enrichment
Скилл выстраивает пайплайн для массового обновления данных в HubSpot. Вместо медленного цикла из поиска и создания записей команда hubspot objects upsert принимает поток JSONL через stdin и пачками по 100 строк вносит изменения по естественному ключу. Для контактов сопоставление идёт по email, для компаний — по domain. Перед выполнением естественный ключ обязательно переводится в нижний регистр, потому что совпадение в CRM должно быть точным.
Перед реальной записью выполняется предварительный прогон с флагом --dry-run. Он отдаёт параметры --digest и --confirm, равный числу строк, которые затем передаются в финальную команду для подтверждения. Ответы на каждую запись разделяются через jq на успешные и ошибочные.
Если ключ не является системным свойством, скилл предлагает поиск через повторяющийся флаг --filter. Здесь действует строгое ограничение CRM: не более 5 групп OR на один запрос.
Какие задачи решает
- обновить контакты в HubSpot из внешнего CSV без цикла поиска
- сопоставить компании из JSONL по домену и дополнить их свойства
- протестировать массовую вставку в CRM через предварительный
--dry-run - отфильтровать ошибки пакетного импорта с кодами 400 и 429
Примеры запросов агенту
- Сделай dry-run апсерта контактов из файла
external.jsonlв HubSpot по полю email - Обогати компании в HubSpot из
leads.csv, сопоставив записи по домену - Раздели результат импорта
results.jsonlна успешные строки и ошибки через jq
Как установить data-enrichment
1Через Skills CLI (Vercel)
npx skills add HubSpot/agent-cli-skills --skill data-enrichment -a claude-code2Вручную: положить файлы
~/.claude/skills/data-enrichment/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/data-enrichment/
1Через Skills CLI (Vercel)
npx skills add HubSpot/agent-cli-skills --skill data-enrichment -a codex2Вручную: положить файлы
.agents/skills/data-enrichment/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add HubSpot/agent-cli-skills --skill data-enrichment -a cursor2Вручную: положить файлы
.cursor/skills/data-enrichment/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install HubSpot/agent-cli-skills data-enrichment2Вручную: положить файлы
.github/skills/data-enrichment/SKILL.md1Через Skills CLI (Vercel)
npx skills add HubSpot/agent-cli-skills --skill data-enrichment -a gemini-cli2Командой платформы
gemini skills install https://github.com/HubSpot/agent-cli-skills --path data-enrichmentВручную: положить файлы
.devin/skills/data-enrichment/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add HubSpot/agent-cli-skills --skill data-enrichment -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о data-enrichment
Как установить data-enrichment в Claude Code?
Выполните в терминале команду npx skills add HubSpot/agent-cli-skills --skill data-enrichment -a claude-code. Также файл скилла можно положить напрямую в ~/.claude/skills/data-enrichment/SKILL.md.
Зачем нужен скилл data-enrichment?
Он автоматизирует массовый импорт и обновление контактов или компаний в HubSpot за один вызов CLI. Команда отправляет данные пачками по 100 записей через hubspot objects upsert, исключая гонки и лишние запросы.
Какие есть ограничения при поиске без upsert?
При поиске через флаг --filter HubSpot разрешает максимум 5 условий OR в одном запросе. Если условий больше пяти, CRM вернёт ошибку 400, поэтому скилл разбивает список значений через xargs -n5.
Исходный текст
SKILL.md · Apache-2.0
---
name: data-enrichment
description: Match external CSV/JSONL records to CRM contacts (by email) or companies (by domain) and write enriched data back in one pass using `hubspot objects upsert`.
triggers:
- "spreadsheet to CRM"
- "match contacts by email"
- "match companies by domain"
- "enrich CRM from CSV"
- "CRM write-back"
- "create or update by email"
---
Prereq: read `bulk-operations/SKILL.md` first — JSONL piping, dry-run/digest, history, and rate-limit hygiene live there. This skill is the upsert-by-natural-key workflow on top.
## The core move: upsert, not search-then-create
`hubspot objects upsert --type X --id-property <natural-key>` reads JSONL on stdin and creates-or-updates each row in **one CLI call** (the CLI batches 100 rows per API request), keyed by a property (email for contacts, domain for companies). No race window, no branching. Do not loop `search` → empty? → `create`.
Per line in: `{"id":"[email protected]","properties":{"firstname":"Jane","jobtitle":"VP"}}`
Per line out: `{"id":"123","ok":true,"data":{...}}` or `{"ok":false,"error":{...}}`. Order matches input. The CLI adds no fields of its own — `data` is the raw batch-upsert API result row.
## CSV/JSONL → upsert stream
Reshape with `jq`, preview with `--dry-run`, then execute. `upsert` is irreversible, so the execute step re-pipes the SAME inputs plus the `--digest`/`--confirm` lifted from the preview line (upsert confirm = the row count, always). Always lowercase the natural key — CRM match is exact. Confirm available property names with `hubspot properties list --type contacts`; never hard-code a list. See `bulk-operations/resources/json-patterns.md` for reshape idioms.
```bash
# CSV → JSONL (any tool); example using csvkit
csvjson external.csv | jq -c '.[]' > external.jsonl
# Preview
cat external.jsonl \
| jq -c '{id:(.email|ascii_downcase), properties:{firstname:.first, lastname:.last, jobtitle:.title, company:.company}}' \
| hubspot objects upsert --type contacts --id-property email --dry-run \
| tee /tmp/upsert.preview.jsonl
# Lift the digest + confirm (present at every row count; upsert confirm = the row count)
digest=$(jq -r 'select(.digest != null) | .digest' /tmp/upsert.preview.jsonl)
confirm=$(jq -r 'select(.digest != null) | .target.id' /tmp/upsert.preview.jsonl)
# Execute — same pipeline, plus --digest/--confirm, capture results
cat external.jsonl \
| jq -c '{id:(.email|ascii_downcase), properties:{firstname:.first, lastname:.last, jobtitle:.title, company:.company}}' \
| hubspot objects upsert --type contacts --id-property email --digest "$digest" --confirm "$confirm" \
| tee /tmp/upsert.results.jsonl
```
Companies: swap `--type companies --id-property domain` and reshape with `.domain|ascii_downcase` as `id`.
## Handle per-record OK / error output
Split with `jq`, inspect failure modes, retry just the failures after fixing the inputs:
```bash
jq -c 'select(.ok==true)' /tmp/upsert.results.jsonl > /tmp/upsert.ok.jsonl
jq -c 'select(.ok==false)' /tmp/upsert.results.jsonl > /tmp/upsert.failed.jsonl
jq -r '.error.status' /tmp/upsert.failed.jsonl | sort | uniq -c # status → count
```
The CLI does not tag rows as created-vs-updated. If the batch-upsert API result row carries a `new` boolean, `jq -r '.data.new' /tmp/upsert.ok.jsonl | sort | uniq -c` splits them; otherwise compare `.data.createdAt` against `.data.updatedAt`.
429s: split the input and rerun smaller chunks (see `bulk-operations` rate-limit notes). 400s usually mean a bad property name or invalid enum value — fix the reshape, rerun the failed inputs.
## Destructive-op safety
`upsert` itself is non-destructive, but write-back can clobber populated fields. Always `--dry-run` first and spot-check. For bulk delete or overwrite of existing data, follow the dry-run → digest → confirm flow in `bulk-operations/SKILL.md`. Recovery: `hubspot history --since 1h`.
## Match without upsert: OR-search → update
When you only want to read matches (no write-back), or the natural key isn't a CRM property, use repeated `--filter` flags — each flag is one OR group.
Verified cap: **5 OR groups per call**. 6+ returns `400 too many filterGroups (count: N, max allowed: 5)`. Chunk 5 at a time:
```bash
# emails.txt: one lowercased email per line
xargs -n5 < emails.txt | while read -r e1 e2 e3 e4 e5; do
args=()
for e in "$e1" "$e2" "$e3" "$e4" "$e5"; do [ -n "$e" ] && args+=(--filter "email=$e"); done
hubspot objects search --type contacts "${args[@]}" --properties email,firstname,company
done > /tmp/matches.jsonl
jq -c '{id, properties:{lifecyclestage:"marketingqualifiedlead"}}' /tmp/matches.jsonl \
| hubspot objects update --type contacts --dry-run
```
For larger keyed enrichments, prefer `upsert` — one pipeline, no chunking math.
© HubSpot · Apache-2.0 · HubSpot/agent-cli-skills