Skip to content

Contact imports

A ContactImport is a named bulk insert of contacts. It’s the typical way to onboard a list — one POST creates all the contacts and the import becomes the unit you reference when enrolling a campaign.

POST /contact_imports
Authorization: Bearer <key>
Content-Type: application/json
Idempotency-Key: optional-stable-key
{
"contact_import": {
"name": "Q2 OC contractors",
"contacts": [
{ "email": "[email protected]", "first_name": "Ada", "last_name": "Lovelace", "company": "Acme" },
{ "email": "[email protected]", "first_name": "Babs", "company": "Acme" }
]
}
}

Duplicate emails (across all your imports) are silently skipped — the response includes a skipped count when this happens.

{ "id": "...", "name": "Q2 OC contractors", "record_count": 2 }

If some contacts were skipped due to duplicates:

{ "id": "...", "name": "Q2 OC contractors", "record_count": 1, "skipped": 1 }
GET /contact_imports
Authorization: Bearer <key>
[
{ "id": "...", "name": "Q2 OC contractors", "record_count": 9529, "created_at": "..." }
]
GET /contact_imports/:id
{
"id": "...",
"name": "Q2 OC contractors",
"record_count": 9529,
"contact_count": 9529,
"created_at": "..."
}

contact_count is the live count of associated Contact rows; record_count is the count at insertion time. They diverge if you delete contacts from the import after creation.

GET /contact_imports/:id/contacts
[
{ "id": "...", "email": "[email protected]", "first_name": "Ada", "company": "Acme", "unsubscribed_at": null, "created_at": "..." }
]
DELETE /contact_imports/:id

Cascades to all associated contacts (which in turn cascades to their enrollments).

StatusCodeWhen
404contact_import_not_foundUnknown id or another account’s.
422no_contacts_providedPOST with empty contacts array.
422validation_failedMissing import name or other validation.