Skip to main content

CDP sync run log

When a CRM or reverse-ETL destination stops landing data, the sync run log is the first surface to open. It is a read-only, per-destination history of every sync run — and a drilldown into the failed rows of any single run, grouped by the cause of the failure. Two endpoints back the surface:
  • GET /api/v1/cdp/sync-run-log/runs — the run list plus a per-destination health summary.
  • GET /api/v1/cdp/sync-run-log/runs/:runId — one run’s failed rows, bucketed by error class.
This guide walks both, shows how to read the error buckets, and closes with a production checklist. For the destinations themselves, read the reverse ETL and warehouse exports operator walkthrough.

1. What the run log is

Every CRM/reverse-ETL destination run — Salesforce, HubSpot, or a warehouse you sync into — appends one entry to the run history. Each entry carries:
  • Identity — id, destination, the object type and the segment the run synced with, plus the run’s start/finish timestamps.
  • Status — ok, partial, failed, or skipped.
  • Record-delta breakdown — attempted (records that produced an upsert candidate), delivered (records in successfully dispatched batches), rejected (rows dropped before dispatch because the upsert identifier was missing or empty), and failed (records in batches whose dispatch failed).
  • Error classes — the distinct failure classes present in the run, so the log line already tells you whether a partial run was a rate limit or a broken field map.
On top of the list, the summary rolls each destination up into a health row: the last run and last success timestamps, the total run count, and two flags — failing (the most recent run ended failed or partial) and overdue.

The freshness SLA

A destination is overdue when its last successful run is older than 26 hours — or when it has runs but has never succeeded. The window leaves a full day of slack for a nightly cadence before flagging, so an on-demand-only destination is not spuriously alarmed unless it is also failing. Watch overdue together with failing: a destination that is overdue and failing is down; a destination that is merely overdue is a scheduler that stopped running.

2. Prerequisites

  • An owner, admin, or developer role on the organization — the log is an operator surface and rejects member roles.
  • An API key with that role, or a dashboard session with it.
  • At least one CRM/reverse-ETL destination configured and run — the endpoints return an empty list when no destination has ever synced.
Both endpoints are read-only and rate-limited to 60 requests per minute, which suits dashboards and polling loops; it is not a bulk-export path.

3. Reading the run list

GET /api/v1/cdp/sync-run-log/runs returns the recent runs (newest first), plus the per-destination summary. All filters are optional:
  • destination — salesforce, hubspot, or another destination id.
  • status — ok, partial, failed, or skipped.
  • limit — 1–200, default 50.
Response shape (trimmed):
Read the summary first. needs_attention: true tells you any destination is failing or overdue before you look at a single run; the per-destination row then points you at the destination to filter on. Only then open the run list for the concrete run ids.

4. Drilling into a run

GET /api/v1/cdp/sync-run-log/runs/:runId returns one run with its failed rows grouped into buckets by error class. Each bucket carries:
  • class — the stable taxonomy value (see the next section).
  • rows — records attributed to this class.
  • occurrences — distinct failure events (failed batches or rejected rows).
  • retryable — whether a bare retry could clear it without operator action.
  • samples — a bounded sample (up to five, each trimmed) of the transport error messages in this bucket.
An unknown run id returns 404 with code SYNC_RUN_NOT_FOUND.
The bucket list is the drilldown: 9 rows never left the platform (a missing upsert key — a mapping problem), and 3 rows were throttled by Salesforce (a cadence problem). Two different fixes; the log tells you which is which without reading raw provider logs.

5. Diagnosing the failure classes

Every failure lands in one of nine classes, and the class decides the retry loop. Retryable classes may clear on their own; the rest need an operator change first — retrying them just fails again. Two of these have a fixed shape worth memorizing:
  • validation buckets from rejected rows always carry the sample “Row dropped: missing or empty upsert identifier value” — the row left the segment but had no value in the mapped upsert-identifier field. Fix the mapping (or backfill the field at the source) before the next run.
  • Legacy runs (from before the enriched history wrote per-batch failures) attribute the run’s failed rows to the class of the single last-recorded error. The buckets still work — they just have coarser samples.

6. Worked example: a failed Salesforce auth run

A nightly Salesforce Contact sync stops landing. The summary row shows failing: true, so filter the list to that destination:
The newest run comes back with "error_classes": ["auth"] and "rows": { "attempted": 4120, "delivered": 0, "rejected": 0, "failed": 4120 }. Drill it:
The bucket says retryable: false, so re-running changes nothing. Reconnect the destination from the CDP integrations page with a fresh Salesforce credential, then trigger a run and confirm:
Expect status: "ok" and delivered equal to attempted. The summary row flips back to failing: false and overdue: false once a success lands.

7. Security: no secret material in the log

The buckets contain no payload chain. Samples carry transport error copy only — never contact records, never field values, never credentials — and each sample is truncated so the detail payload stays bounded. The endpoints read the run history projection and nothing else, so the log is safe to share with an audit channel while still naming the cause of the failure. Credentials for the destinations themselves remain write-only on the config surfaces; the run log never touches them.

8. Production checklist

  • Check the summary’s needs_attention flag before scanning individual runs.
  • Treat failing && overdue as down; treat overdue alone as a stopped schedule.
  • Read the bucket class before retrying: retry only rate_limit, network, timeout, server, and (once) unknown.
  • Fix auth, permission, validation, and not_found at the destination, the mapping, or the record — then re-run.
  • On rate_limit, slow the destination’s cadence instead of hammering retries.
  • Keep polling within the 60-requests-per-minute per-endpoint limit.
  • Route the destination’s config edits through the CDP integrations page; the run log is read-only by design.