Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/DataTalksClub/datamailer/llms.txt

Use this file to discover all available pages before exploring further.

POST /api/contacts/imports/csv accepts a UTF-8 CSV file and imports each row as a contact upsert. The endpoint uses the same idempotency and partial-error logic as the JSON import: valid rows are committed regardless of which other rows fail, and re-uploading the same file is safe. The CSV must include an email header column; all other supported columns are optional and can be omitted entirely.

Authentication

All requests must include a Bearer token issued for the target client.
Authorization: Bearer <client-api-key>

Request

Method and path: POST /api/contacts/imports/csv Content-Type: multipart/form-data Upload the file using the file form field. The audience and client slugs can be provided as additional form fields (or as columns in the CSV itself).

Form Fields

file
file
required
The CSV file to import. Must be UTF-8 or UTF-8 with BOM. The first row must be a header row containing at least the email column.
audience
string
Default audience slug applied to all rows that do not have an audience column, or whose audience column is empty.
client
string
Default client slug applied to all rows that do not have a client column, or whose client column is empty.
dry_run
string
Set to true to validate all rows and return projected counts without writing any data. Accepts true, 1, yes. Defaults to false.

Supported CSV Columns

The following column names are recognized. Columns not listed here are silently ignored during import.
ColumnTypeNotes
emailstringRequired. Contact email address.
audiencestringAudience slug. Falls back to the audience form field when blank.
clientstringClient slug. Falls back to the client form field when blank.
tagsstringSemicolon-separated tag names, e.g. course-ml-zoomcamp;course-de-zoomcamp.
subscription_statusstringSubscription status: pending, subscribed, or unsubscribed. Also accepted as status.
verifiedbooleanSee boolean values below.
global_unsubscribedbooleanSets global_unsubscribed_at if true.
hard_bouncedbooleanSets hard_bounced_at if true.
complainedbooleanSets complained_at if true.
unsubscribedbooleanWhen true, overrides subscription_status to unsubscribed.
email_validation_statusstringOne of unknown, valid, invalid_syntax, no_mx, disposable, risky, manually_invalid, externally_validated.
email_validation_reasonstringFree-text reason stored alongside the validation status.
email_validated_atstringISO 8601 timestamp of the external validation. Set automatically when status or reason changes.
unsubscribe_reasonstringStored as the unsubscribe reason when status is unsubscribed.

Boolean Column Values

Boolean columns accept the following values (case-insensitive):
  • True: true, yes, 1, y, on
  • False: false, no, 0, n, off, or empty string

Tags Format

Provide multiple tags as a semicolon-delimited list within a single cell:
course-ml-zoomcamp;course-de-zoomcamp;mlops-zoomcamp
Tags are slugified and created automatically if they do not already exist in the audience.

Deduplication

Within a single uploaded file, the first valid row for a given normalized email wins. Later rows with the same normalized email are skipped and reported as duplicate rows. Emails are normalized by lowercasing before comparison.

Response

HTTP 200 OK. The response shape is identical to the JSON import response.
dry_run
boolean
Echoes the dry_run flag.
counts
object
results
object[]
Per-row result entries for all successfully processed rows.
errors
object[]
Per-row error entries for rows that failed validation.

Example

CSV File (contacts.csv)

email,tags,subscription_status,verified,global_unsubscribed
learner@example.com,course-ml-zoomcamp;course-de-zoomcamp,subscribed,true,false
student@example.com,course-de-zoomcamp,subscribed,false,false
bounced@example.com,,pending,false,false

Request

curl -X POST https://datamailer.example.com/api/contacts/imports/csv \
  -H "Authorization: Bearer dm_dtcnews_demo_newsletter_import_export_key" \
  -F "file=@contacts.csv" \
  -F "audience=dtc-courses" \
  -F "client=dtc-courses"

Response

{
  "dry_run": false,
  "idempotency_key": "",
  "counts": {
    "total": 3,
    "created": 2,
    "updated": 1,
    "unchanged": 0,
    "skipped": 0,
    "invalid": 0
  },
  "results": [
    {
      "index": 0,
      "item": 1,
      "email": "learner@example.com",
      "action": "updated",
      "contact": { "contact_id": 42, "email": "learner@example.com" }
    },
    {
      "index": 1,
      "item": 2,
      "email": "student@example.com",
      "action": "created",
      "contact": { "contact_id": 43, "email": "student@example.com" }
    },
    {
      "index": 2,
      "item": 3,
      "email": "bounced@example.com",
      "action": "created",
      "contact": { "contact_id": 44, "email": "bounced@example.com" }
    }
  ],
  "errors": []
}
audience and client can be provided either as form fields (shown above) or as columns within the CSV itself. If a row includes both a CSV column value and a form field default, the CSV column takes precedence for that row. The authenticated client slug must always match — rows referencing a different client return a forbidden validation error.

Build docs developers (and LLMs) love