Import contacts

POST/api/v1/contacts/import
Requires anorganization API keywith the scopecontacts:write

Bulk-create or update contacts. Existing contacts are matched on email address and updated rather than duplicated.

Request

POST/api/v1/contacts/import
curl -X POST 'https://app.mailyte.com/api/v1/contacts/import' \
  -H 'Authorization: Bearer mk_live_YOUR_API_KEY'
The key names its own organization, so no X-Organization-ID header is needed.

Response

Success.

  • dataobject

    The counters always sum: `imported + skipped_invalid + skipped_duplicate == total`.

    • objectstringcontact_import
    • totalinteger

      Rows we were given. For a CSV this is DATA rows — a recognised header row is not counted.

    • importedinteger

      Rows that became new contacts.

    • skipped_invalidinteger

      Rows with a missing or unparseable email address, or with no value for a field this organization marked required. An invalid row is skipped and counted, never fatal: a 10,000-row file with one bad address still imports 9,999.

    • skipped_duplicateinteger

      Addresses already known to this organization, plus repeats within the batch itself. Import is insert-or-SKIP, not an upsert — an address already on file keeps its name, its custom fields and, most importantly, its state, so re-importing a list can never resurrect somebody who unsubscribed.

    • error_countinteger

      How many rows failed in total. Equal to `skipped_invalid + skipped_duplicate`, and the number to trust — `errors` below is capped, so a file where everything failed reports 4,000 here and lists 100.

    • errorsarray<object>

      The failed rows, in FILE ORDER, so you can read this beside your spreadsheet. Capped at 100: the first hundred show the pattern — a mis-mapped column, a stray header — and `error_count` says how many there really were. An empty array means every row was accepted.

      • rowinteger

        1-based, counting DATA rows, so it lines up with your spreadsheet after the header.

      • emailstring

        The address exactly as you sent it, before normalising — empty when the row had none.

      • reasonstringmissing_email | invalid_email | duplicate_in_file | already_exists | missing_required_field

        Machine-readable. `duplicate_in_file` and `already_exists` both count toward `skipped_duplicate` but are different problems with different fixes: one is a typo in your file, the other is a contact you already have. `missing_required_field` means your organization marked a contact field required and this row has no value for it — the fix is a column, not an address, and `message` names which one.

      • messagestring

        The same thing in words, safe to show a person. For a duplicate it names the earlier row.

Returned inside the standard envelope.

Errors

StatusWhen
401The API key is missing, unknown, revoked or expired. All four answer identically, on purpose: distinguishing them would confirm which keys exist.
403The key is valid but may not do this: it lacks the required scope, its IP allowlist does not include you, or this endpoint does not accept API keys.
404No such resource in this organization.
422The request was understood but the values were not acceptable.
429Too many requests, or the organization has spent its sending allowance. `Retry-After` says how long to wait.

Every status, with what causes it and what to do, is on the error reference.