Guides
Data health
Data health is a loop: scan a dimension of catalog quality, read the issues behind its numbers, then remediate them in draft-only batches. These are the first non-GET endpoints on v1 — and they are built so a stale batch can never clobber newer values.
The loop at a glance
POST /api/v1/data-health/scans # recompute one dimension (pim:write)
GET /api/v1/data-health # read the cached snapshot (pim:read)
GET /api/v1/data-health/issues?section=... # the entities behind it (pim:read)
POST /api/v1/data-health/completeness-fills # fill empty fields, drafts (pim:write)
POST /api/v1/data-health/divergence-reconciles # harmonize stores, drafts (pim:write)
Read the cached snapshot
/api/v1/data-healthpim:read
The account's catalog-quality state per dimension, as of the last scan.
Data health is derived, re-computable state, so this read never scans — it is
cheap to poll. Scope to one store with store_id; omitted, the answer
is account-wide.
curl -s https://api.peak-pim.com/api/v1/data-health \
-H "Authorization: Bearer pk_live_..."
{"dimensions": [
{"dimension": "completeness", "scanned": true,
"result": {...}, "scanned_at": "2026-07-30T06:00:04Z"},
{"dimension": "divergence", "scanned": false}
]}
It means no scan has ever run for that dimension — a different answer from "scanned, and clean". Each dimension's result is an opaque object whose shape is specific to that dimension and may gain fields within v1.
Run a scan
/api/v1/data-health/scanspim:writeRecompute one dimension and cache the result. This is the expensive path — it scans the catalog — so it is a request, never something to poll. It touches no merchant data.
curl -s -X POST https://api.peak-pim.com/api/v1/data-health/scans \
-H "Authorization: Bearer pk_live_..." \
-H "Content-Type: application/json" \
-d '{"dimension": "completeness"}'
{"dimension": "completeness", "scanned": true,
"result": {...}, "scanned_at": "2026-07-31T09:12:40Z"}
Add store_id to scope the scan to one store, where the dimension is
store-scoped. The response is the fresh snapshot for that dimension.
Page through the issues
/api/v1/data-health/issuespim:read
One page of one section's issue rows — the entities behind a dimension's numbers.
Unlike every other list on v1, this one is page-numbered
(page, page_size defaulting to 25 and clamped to 100,
has_next) rather than cursor-paged, because a section is a live query
over changing state rather than a stable list to walk.
curl -s "https://api.peak-pim.com/api/v1/data-health/issues?section=completeness-required&page=1&page_size=100" \
-H "Authorization: Bearer pk_live_..."
{"section": "completeness-required",
"data": [
{"entity_id": "0e8f...", "entity_type": "product", "title": "Trail Runner 2",
"store_id": "c3d4...", "fields": ["body_html", "vendor"]}
],
"page": 1, "has_next": false}
section is required and names which issue list to read — for example
sync-failed, completeness-required,
divergence, media-unused, or
translations-untranslated. Narrow with store_id, and with
locale on the translation sections. The divergence section also
reports compared — how many multi-store entities the scan compared to
produce the list.
Remediate: fill empty fields
/api/v1/data-health/completeness-fillspim:writeWrite authored values into fields that are still empty, up to 500 operations per batch. Each operation names one entity, one store, one field, and the value to write:
curl -s -X POST https://api.peak-pim.com/api/v1/data-health/completeness-fills \
-H "Authorization: Bearer pk_live_..." \
-H "Content-Type: application/json" \
-d '{
"entity_type": "product",
"operations": [
{"entity_id": "0e8f...", "store_id": "c3d4...",
"field": "vendor", "value": "Peak Outfitters"}
]
}'
{"filled": 1,
"results": [
{"entity_id": "0e8f...", "store_id": "c3d4...", "field": "vendor",
"ok": true, "filled": true}
]}
- Partial success is normal. A 200 means the batch ran, not that every operation succeeded — read
resultsper operation, never just the status code. - Re-check-before-write. The batch re-checks the condition immediately before writing: a field filled by someone else since the last scan is skipped, never overwritten.
ok: truewithfilled: falsemeans exactly that — the gap is settled, but this batch didn't write it. - Drafts only. Nothing reaches Shopify; publishing stays an explicit follow-up.
Remediate: harmonize diverging stores
/api/v1/data-health/divergence-reconcilespim:write
Bring one field into agreement across stores. Each operation names an entity and a
field, plus exactly one of winner_store_id (adopt that store's value)
or value (write an explicit one; add store_id to narrow
it to one store). Stores that already match are left untouched.
curl -s -X POST https://api.peak-pim.com/api/v1/data-health/divergence-reconciles \
-H "Authorization: Bearer pk_live_..." \
-H "Content-Type: application/json" \
-d '{
"entity_type": "product",
"operations": [
{"entity_id": "0e8f...", "field": "title", "winner_store_id": "a1b2..."}
]
}'
{"patched": 1,
"results": [
{"entity_id": "0e8f...", "store_id": "c3d4...", "ok": true}
]}
Same rules as fills: partial success is normal, writes are drafts only, and the
divergence is re-checked immediately before writing. Both remediation endpoints
can also answer 423 pim_locked while a store import or refresh is
being reconciled — retry after the merchant finishes the review.
Reserved: unused deletions
/api/v1/data-health/unused-deletionspim:delete
The one destructive route is documented but reserved, not reachable:
it requires pim:delete, which is not granted by the consent flow or by
key minting, so it answers 403 for every credential that exists today.
It is documented so the contract is honest about what the endpoint would do — delete
entities across every store they live on, re-verifying each candidate as
still-unused immediately before deletion.
The MCP equivalents
The connector carries the whole loop. Note the vocabulary shift: MCP tools say
master_id / channel_id / winner_channel_id
where REST says entity_id / store_id /
winner_store_id.
| Tool | Scope | Answers / does |
|---|---|---|
get_data_health_v1 | pim:read | The cached per-dimension state — sync, completeness, media, structure, cross-store divergence, translations — as of the last scan |
list_data_health_issues_v1 | pim:read | One section's issue rows, paged with page/page_size, narrowed by channel_id and locale |
run_data_health_scan_v1 | pim:write | Recompute one dimension and cache the result |
fill_completeness_gaps_v1 | pim:write | Batch-fill still-empty fields as drafts (up to 500 operations) |
reconcile_value_divergence_v1 | pim:write | Batch-harmonize a field across stores as drafts |
There is no delete tool on the connector at all. After remediation, drafts go live
only through an explicit publish — publish_master_v1 on the publish
tier, per item.
Related
- Data health reference — full request and response schemas.
- Audit drift across stores — the manual version of the divergence dimension.
- Build an AI catalog operator — running this loop from an assistant.