Guide
Syncing data
Triggering bounded extraction runs, idempotency, and what each run state means.
A sync run extracts one window from the provider, stores immutable raw evidence, loads it, and publishes the days it wins. Reading is separate: publication is what makes data visible to queries.
This route triggers runs explicitly. The controlled pilot scheduler is implemented and locally tested separately; new live automation acceptance remains pending.
POST /v1/syncsAuthorization: Bearer <key with sync:write>Content-Type: application/jsonIdempotency-Key: nightly-ga4-2026-09-06{ "workspaceId": "your_workspace", "connectionId": "your_connection", "propertyId": "123456789", "startDate": "2026-08-31", "endDate": "2026-09-04"}Same five window fields as a query, without metric and dimensions.
Idempotency
Section titled “Idempotency”Idempotency-Key is required: 8–128 characters from A-Za-z0-9._:-.
Reusing a key with the same window resumes or returns the existing durable run rather than starting a second one. This is what makes retrying safe: a run that crashed after committing raw evidence resumes from that evidence instead of re-fetching, so a retry does not spend provider quota twice or risk a different result.
Reusing a key with a different window is 409 idempotency_conflict. Changed intent
needs a new key.
Know the boundary of this guarantee. At most one live lease can execute an exact
workspace/connection/property/date window. A different key that contends while that
lease is live gets retryable 409 window_in_progress before a run or provider attempt
is allocated. Retry that same key after the owner finishes or expires. Partly
overlapping windows remain independent; use a new key for a deliberate correction.
Runs created for an internal schedule belong to that schedule’s pipeline rather than the public sync namespace. Public create/replay/takeover and polling do not return or acquire a pipeline-owned run, even if its identifier becomes known. Ownership is rechecked at each effect, checkpoint, publication, failure/cleanup mutation and final-result boundary, so a pipeline attached after an earlier read still withholds later work and the response from the public caller.
Publication checks both quantities atomically. The immutable extraction timestamp is primary, so a genuinely later extraction may correct an earlier head even when its run was created first. Only equal-millisecond extraction timestamps use the creation sequence as a stable tie-break. Frozen older evidence cannot win merely because its run was created later.
Window rules
Section titled “Window rules”The window must contain 1 to 31 days and end before today in the property’s
returned timezone. Strict date, interval and identifier validation remains at the
application boundaries; there is no earlier UTC completion gate. A day still in
progress is 400 incomplete_day.
Local tests cover Oslo summer and winter midnight, and a Los Angeles date that has
ended in UTC but remains incomplete locally. The earlier UTC-gate defect described
by the original reference at 1274bce is corrected in stage 2 source.
Response and run states
Section titled “Response and run states”POST /v1/syncs returns 200 for completed, partial or superseded, and 202
for an in-progress run. A failed POST returns a classified 4xx or 5xx error.
Poll with GET /v1/syncs/{runId}. A successful GET returns 200 for the current run
record, including in-progress and failed states. Inspect state to determine
progress and errorCode to diagnose a recorded failure.
The example below is a real run recorded on staging on 7 September 2026.
{ "runId": "78d95c71-2f6e-4b35-8729-6d8d4b6ecc86", "state": "completed", "extractionSeq": 9, "completedAt": 1788754811781, "timezone": "Europe/Oslo", "quality": { "subjectToThresholding": false, "dataLossFromOtherRow": false }, "publishedDates": 5, "usage": { "providerRequests": 1, "providerRows": 5, "rawBytes": 615, "loadRequests": 1, "loadedRows": 5 }}| State | Meaning |
|---|---|
pending extracting raw_committed loaded | In progress (POST 202; polling GET 200). raw_committed means the provider result is durably stored — a retry from here will not re-fetch. |
completed | The reliable run owned every requested date when its publication transaction completed. |
partial | Provider thresholding, sampling or other-row loss prevented trusted publication. No dates from this report replace published data. |
superseded | A reliable run did not own its whole requested window. It may have published some dates or none; publishedDates gives the exact count. |
failed | See errorCode. |
partial does not mean partial success
Section titled “partial does not mean partial success”This is the state most likely to be misread, so it is worth being blunt about.
A run is marked partial when the provider report was thresholded, sampled or
lossy. In that case the publication step emits no rows at all: publishedDates is
0, nothing became visible, and the previous complete result is left exactly as it
was. It is a refusal, not a partial write.
The right response is to re-run the window later, once the provider returns a clean report. Data volume, GA4’s thresholding behaviour and the size of the window all affect whether it will.
Check publishedDates, not only state
Section titled “Check publishedDates, not only state”A POST 200 means terminal, not successful full publication; a polling GET 200
only means the run record was read. Accept completed
with the expected publishedDates for a whole-window refresh. The scheduler also
checks the query’s run identities before export. A terminal replay returns the
recorded result; later corrections may already own its former dates.
Quality gates
Section titled “Quality gates”Only a completely fetched, unthresholded, unsampled, loss-free report may advance
published data. A report that is thresholded, sampled or lossy leaves the last
complete result intact and records what was seen in quality — that is the partial
state above. It does not half-publish and it does not overwrite good data with worse
data.
This is deliberately conservative for a first connector and does not set the tolerance policy for later ones.
Usage counters
Section titled “Usage counters”usage counts what the run actually spent, including attempts rejected before the
provider was called. These are per-run figures for operational visibility; they are
not a billing surface.
Failures
Section titled “Failures”A failed run publishes nothing. The code says which trust condition failed rather
than collapsing to a generic error — provider_auth_failed, restricted_report,
changing_report, row_count_mismatch, raw_integrity_failure and others are all
distinct. See Errors for the full list and which are retryable.
Retry a retryable failure with the same idempotency key so the run resumes rather than duplicating work.