MisarMisar Docs
MisarMailMisarBlogMisarReachMisarPostMisarDevMisarCoderMisarSEOMisar PlatformMisar SSO
Contacts

Import from File

POST /io/contacts/contacts/file-import — upload a CSV or Excel file and upsert contacts, with a post-save format report.

Uploads a .csv, .xls, or .xlsx file and upserts every parsed row into the contacts table. Use this when a human hands you a spreadsheet; use POST /contacts/import when you already have structured JSON.

POST https://api.misar.io/io/contacts/contacts/file-import

Save first, validate second

Rows are written to the database before format validation runs. Contacts with soft format issues (an over-long company name, an unexpected status) are still saved, and the issues come back in validation_errors so you can correct them afterwards. Only a file with zero parseable rows is rejected outright.

Authentication

Either mode works:

SSO bearer (own contacts)
Authorization: Bearer <sso_access_token>
Service key (any user)
x-audience-service-key: <AUDIENCE_SERVICE_KEY>

With a service key you must also pass ?user_id=<uuid>.

POST/io/contacts/contacts/file-import

The request body is multipart/form-data, not JSON.

Query parameters

user_idstringquery

Required with a service key; ignored with an SSO bearer. Must be a valid UUID.

Form fields

fileFilebodyrequired

The upload. Extension must be .csv, .xls, or .xlsx. Maximum size 10 MB.

Response fields

successboolean

true when the upsert completed.

total_in_filenumber

Rows the parser saw in the file, including ones it could not turn into a contact.

savednumber

Contacts written to the database.

validation_errorsArray<{row, field, value, message}>

Per-row format issues found after saving. row is the 1-based row index in the source file.

warningsstring

A human-readable summary, present only when validation_errors is non-empty.

Request
curl -X POST "https://api.misar.io/io/contacts/contacts/file-import" \
  -H "Authorization: Bearer $SSO_ACCESS_TOKEN" \
  -F "file=@contacts.csv"
Request (service key)
curl -X POST "https://api.misar.io/io/contacts/contacts/file-import?user_id=3f1a2b4c-5d6e-4f70-8a91-b2c3d4e5f607" \
  -H "x-audience-service-key: $AUDIENCE_SERVICE_KEY" \
  -F "file=@contacts.xlsx"
200 — Clean import
{
  "success": true,
  "total_in_file": 412,
  "saved": 412,
  "validation_errors": []
}
200 — Saved with format issues
{
  "success": true,
  "total_in_file": 412,
  "saved": 412,
  "validation_errors": [
    {
      "row": 87,
      "field": "status",
      "value": "unsub",
      "message": "Invalid status \"unsub\" — must be one of: subscribed, unsubscribed, bounced, complained"
    }
  ],
  "warnings": "1 contact(s) have format issues — they were saved but you may want to correct them"
}
400 — Nothing parseable
{
  "error": "No valid contacts found",
  "parse_errors": ["Row 1: missing email"],
  "total": 3
}

Recognised columns

The parser maps spreadsheet headers onto the standard contact fields. Anything it does not recognise is preserved on custom_fields.

FieldLimit
emailRequired. Must match a basic address shape.
first_name100 chars
last_name100 chars
phone50 chars
company150 chars
job_title100 chars
statusOne of subscribed, unsubscribed, bounced, complained
source100 chars. Defaults to import.
social_profilesPer-network URLs, 500 chars each
custom_fields8 KB serialised

Imported contacts are upserted on (user_id, email), so re-importing a file updates existing rows rather than duplicating them. lead_score is reset to 0 on import.

Status codes

CodeMeaning
200Import ran. Check validation_errors.
400Malformed multipart body, no file field, file over 10 MB, unsupported extension, or no parseable contacts.
401Neither a valid SSO bearer nor a valid x-audience-service-key was supplied.
429Contacts rate limit exceeded (200 ops/user/60 s). Includes a Retry-After header.
500The contacts could not be written.

Limits

LimitValue
Maximum file size10 MB
Accepted extensions.csv, .xls, .xlsx
Request timeout60 s
Rate limit200 ops/user/60 s