Step 1 — Look at what was refused
Start with the jobs feed, scoped to your batch and the outcome you care about:status accepts any import outcome — rejected for rows the provider refused, unresolved for rows whose fate could not be determined, unprocessed for rows a cancellation stopped before they were sent.
This is the quick look: page envelope, newest first, good for a screen. For the whole set — and for the provider’s own reason text, which the JSON feed does not carry — request the export.
Step 2 — Request the export
POST /flows/jobs/export takes the same filters as the listing. At least one is required.
202 comes back immediately — nothing has been generated yet:
Step 3 — Poll until it is ready
status moves pending → running → ready. While it is generating, download_url is null — that is a normal poll, not an error. Once it is ready:
A
failed export says why in error_reason: row_cap_exceeded when the filters matched more rows than one export may contain (the cap is 1,000,000 — a whole bulk batch’s rejected rows always fit), source_unavailable when the underlying records are no longer retained, or generation_failed.
Step 4 — Download the file
download_url is a short-lived signed URL — about fifteen minutes — and it needs no API key of its own. Download it straight away:
Never store or share the URL. It is deliberately short-lived and freely re-fetchable: read the export again at any time for a fresh one, for as long as the export lives. Anything that would need a long-lived link should re-read the export instead.
After the file is deleted, request the export again and it is regenerated from the underlying rows — for as long as those rows are inside your account’s retention window.
Step 5 — Read the file
Every row is the record you uploaded, column for column, followed by the outcome columns:Two header details worth knowing. The record columns come from the rows in the export itself, so a column that only appears on later rows — or one whose name collides with an outcome column, such as your own
status field — is preserved in the _extra_json column rather than dropped or duplicated. And group by reject_class before reading prose: it is the stable, countable field, while trouble_message is written for a person.Step 6 — Fix and re-upload
Correct the offending column in the CSV, drop the outcome columns, and send it back through the normal bulk ingestion path: create a record set, push the corrected rows as chunks, seal, and trigger.Re-uploading corrected rows is a fresh send, and that is the point. Only rows you actually fix should go back. Rows that came back
unresolved are a different decision: nobody knows whether they reached your platform the first time, so re-uploading them accepts a possible duplicate contact. Decide that deliberately.Next steps
Import outcomes
What each outcome means, and how the counters roll up into a batch’s terminal state.
Bulk ingestion
The upload path for the corrected file — manifest, chunks, seal, trigger.