Endpoints
For small synchronous batches (up to 100 records), use
POST /v1/contacts/upsert instead. Imports shine for large files, dedupe control, and auditable per-row results.Start an Import
source:
Upload mode
Returns an
import_session_id plus a presigned S3 upload_url (valid for 1 hour) — PUT your CSV bytes directly to it, then continue to preview/execute.
Inline rows mode
Request bodies are capped at 5 MB. Larger datasets should use upload mode.
Preview an Import
mappings (source header → target field), per-column sample validation issues, the org’s available_fields, and field_creation_candidates for unmapped columns. Optional for headless runs — you can pass mappings straight to execute.
Proposed mappings never send two columns to the same field. When two columns match one field, the column matched by its header name wins over an AI suggestion, then the earlier column wins. The other column is left unmapped with a reason naming the column that claimed the field, so it shows up in field_creation_candidates. For example, in a Shopify customer export, Phone maps to phone and Default Address Phone is left for a custom field.
Execute an Import
Rows matching an existing contact (normalized email or phone) follow
duplicate_strategy; per-column overwrite_behavior (overwrite | skip_if_filled) controls whether filled values are replaced. Returns 202 with an import_run_id.
Poll a Run
status moves queued → running → completed | failed, with created_count, updated_count, skipped_count, error_count, and chunk-level progress.
Per-Row Results
limit ≤ 250), filterable by status (created | updated | skipped | failed); failed rows include field_issues with error codes. Row numbers count the header as line 1.
skipped and failed rows with reasons (Content-Disposition: attachment). Use the paginated results endpoint for full result sets.