Contacts
Contacts API
6 endpoints under https://api.mailneo.co/api/v2. Every request carries your key in the x-api-key header.
Scopes used on this page: contacts:delete, contacts:read, contacts:write. Each endpoint below states the one it requires. Start with the quickstart →
/contactsList contacts
Requires contacts:read
Cursor-paginated. Pass updated_since with the synced_through value from your previous run to fetch only what changed; you will occasionally re-see a row, so upsert by id. Deletes are not visible to updated_since.
Query parameters9
limitintegerdefault: 251..100cursorstring≤ 256 charsnext_cursor. Treat it as a black box: cursors are bound to the sort they were issued under and are validated strictly, so a hand-crafted, modified or foreign cursor — including any cursor issued before the 2026-08 format revision — answers 400 cursor_invalid. On that error, restart from the first page.order"asc" | "desc"default: descupdated_sincestring · date-timesort"updated_at" | "created_at" | "last_activity_at"default: updated_atstatus"ACTIVE" | "CONVERTED" | "LOST" | "UNQUALIFIED" | "ARCHIVED"emailstring≤ 320 charssourcestring≤ 200 charstagsstring≤ 1000 charscurl "https://api.mailneo.co/api/v2/contacts?limit=25&order=desc&sort=updated_at" \
-H "x-api-key: $MAILNEO_API_KEY"{
"data": [
{
"object": "contact",
"id": "c1vouqol1vev4zm1v4vjan1uu",
"email": "ada@example.com",
"first_name": "Ada",
"last_name": "Lovelace",
"company": "Acme Inc",
"phone": "+1 555 0142",
"website": "https://example.com",
"linkedin": "…",
"twitter": "…",
"facebook": "…",
"status": "ACTIVE",
"tags": [
"vip"
],
"source": "api",
"custom_fields": {
"plan": "pro"
},
"emails_opened_count": 128,
"links_clicked_count": 128,
"total_interactions": 1,
"has_replied": false,
"last_contacted_at": "2026-06-24T15:45:00.000Z",
"last_activity_at": "2026-06-24T15:45:00.000Z",
"created_at": "2026-05-11T09:30:00.000Z",
"updated_at": "2026-06-24T15:45:00.000Z"
}
],
"meta": {
"request_id": "17ed0c3e-18ed-4dd1-a5ed-091816ed0aab",
"has_more": false,
"next_cursor": null,
"synced_through": "2026-06-24T15:45:00.000Z"
}
}/contactsCreate a contact
Requires contacts:write · Idempotency-Key optional
Creates one contact. The email is normalised (trimmed and lower-cased) and must be unique within the team — a duplicate is a 409 resource_already_exists, not a silent upsert. Use POST /contacts/bulk when you want upsert semantics.
Query parameters1
dry_runbooleandata: null and a meta.impact block. Requires the resource's :read scope in addition to the scope this endpoint declares. Must be exactly true or false.Header parameters1
Idempotency-Keystring≤ 255 charsIdempotent-Replayed: true. Reusing a key with a different body is a 409 idempotency_key_reused. Keys are remembered for 24 hours.Body parameters13
emailstringrequired3..320 charsfirst_namestring · nullable≤ 200 charslast_namestring · nullable≤ 200 charscompanystring · nullable≤ 200 charsphonestring · nullable≤ 50 charswebsitestring · nullable≤ 500 charslinkedinstring · nullable≤ 500 charstwitterstring · nullable≤ 500 charsfacebookstring · nullable≤ 500 charssourcestring · nullable≤ 200 charsstatus"ACTIVE" | "CONVERTED" | "LOST" | "UNQUALIFIED" | "ARCHIVED"tagsstring[]≤ 50 itemscustom_fieldsobjectcurl -X POST "https://api.mailneo.co/api/v2/contacts" \
-H "x-api-key: $MAILNEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "ada@example.com",
"first_name": "Ada",
"last_name": "Lovelace",
"company": "Acme Inc",
"phone": "+1 555 0142",
"website": "https://example.com",
"linkedin": "…",
"twitter": "…"
}'{
"data": "…",
"meta": {
"request_id": "ecca272b-ebca-4598-aeca-2a51edca28be",
"dry_run": false
}
}/contacts/{contactId}Retrieve a contact
Requires contacts:read
Path parameters1
contactIdstringrequired1..64 charscurl "https://api.mailneo.co/api/v2/contacts/{contactId}" \
-H "x-api-key: $MAILNEO_API_KEY"{
"data": {
"object": "contact",
"id": "c1vouqol1vev4zm1v4vjan1uu",
"email": "ada@example.com",
"first_name": "Ada",
"last_name": "Lovelace",
"company": "Acme Inc",
"phone": "+1 555 0142",
"website": "https://example.com",
"linkedin": "…",
"twitter": "…",
"facebook": "…",
"status": "ACTIVE",
"tags": [
"vip"
],
"source": "api",
"custom_fields": {
"plan": "pro"
},
"emails_opened_count": 128,
"links_clicked_count": 128,
"total_interactions": 1,
"has_replied": false,
"last_contacted_at": "2026-06-24T15:45:00.000Z",
"last_activity_at": "2026-06-24T15:45:00.000Z",
"created_at": "2026-05-11T09:30:00.000Z",
"updated_at": "2026-06-24T15:45:00.000Z"
},
"meta": {
"request_id": "7d051643-7c05-44b0-af05-19697e0517d6"
}
}/contacts/{contactId}Update a contact
Requires contacts:write · Idempotency-Key optional
Merge-patch: fields you omit are left alone, and null clears a field. tags and custom_fields are the exceptions worth knowing — tags REPLACES the whole array, while custom_fields merges key by key so a value written by an import is not lost.
Path parameters1
contactIdstringrequired1..64 charsQuery parameters1
dry_runbooleandata: null and a meta.impact block. Requires the resource's :read scope in addition to the scope this endpoint declares. Must be exactly true or false.Header parameters1
Idempotency-Keystring≤ 255 charsIdempotent-Replayed: true. Reusing a key with a different body is a 409 idempotency_key_reused. Keys are remembered for 24 hours.Body parameters13
emailstring3..320 charsfirst_namestring · nullable≤ 200 charslast_namestring · nullable≤ 200 charscompanystring · nullable≤ 200 charsphonestring · nullable≤ 50 charswebsitestring · nullable≤ 500 charslinkedinstring · nullable≤ 500 charstwitterstring · nullable≤ 500 charsfacebookstring · nullable≤ 500 charssourcestring · nullable≤ 200 charsstatus"ACTIVE" | "CONVERTED" | "LOST" | "UNQUALIFIED" | "ARCHIVED"tagsstring[]≤ 50 itemscustom_fieldsobjectcurl -X PATCH "https://api.mailneo.co/api/v2/contacts/{contactId}" \
-H "x-api-key: $MAILNEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "ada@example.com",
"first_name": "Ada",
"last_name": "Lovelace",
"company": "Acme Inc",
"phone": "+1 555 0142",
"website": "https://example.com",
"linkedin": "…",
"twitter": "…"
}'{
"data": "…",
"meta": {
"request_id": "853dd55c-863d-46ef-a73d-d882883dda15",
"dry_run": false
}
}/contacts/{contactId}Delete a contact
Requires contacts:delete
Permanent. The contact row is removed, and the copy of its name and email retained on campaign recipients is anonymised in the same transaction, so campaign counts survive and that copy does not. There is no undo — call with ?dry_run=true first. This is not a complete erasure request: sent messages retain the delivery address in the sending log. Use the account data-erasure flow for a full GDPR deletion.
Path parameters1
contactIdstringrequired1..64 charsQuery parameters1
dry_runbooleandata: null and a meta.impact block. Requires the resource's :read scope in addition to the scope this endpoint declares. Must be exactly true or false.curl -X DELETE "https://api.mailneo.co/api/v2/contacts/{contactId}" \
-H "x-api-key: $MAILNEO_API_KEY"{
"data": {
"object": "contact",
"id": "c1vouqol1vev4zm1v4vjan1uu",
"deleted": true
},
"meta": {
"request_id": "bc579276-bd57-4409-aa57-8f50bb5790e3",
"dry_run": false
}
}/contacts/bulkCreate or update many contacts
Requires contacts:write · Idempotency-Key required
Upserts up to 100 contacts by email, in one transaction: either every row is written or none is. Idempotency-Key is required, because a retried batch that ran the first time must not run twice. Existing contacts are merge-patched exactly as PATCH /contacts/{contactId} would patch them.
Query parameters1
dry_runbooleandata: null and a meta.impact block. Requires the resource's :read scope in addition to the scope this endpoint declares. Must be exactly true or false.Header parameters1
Idempotency-Keystringrequired≤ 255 charsIdempotent-Replayed: true. Reusing a key with a different body is a 409 idempotency_key_reused. Keys are remembered for 24 hours.Body parameters14
contactsobject[]required1..100 itemscontacts[].emailstringrequired3..320 charscontacts[].first_namestring · nullable≤ 200 charscontacts[].last_namestring · nullable≤ 200 charscontacts[].companystring · nullable≤ 200 charscontacts[].phonestring · nullable≤ 50 charscontacts[].websitestring · nullable≤ 500 charscontacts[].linkedinstring · nullable≤ 500 charscontacts[].twitterstring · nullable≤ 500 charscontacts[].facebookstring · nullable≤ 500 charscontacts[].sourcestring · nullable≤ 200 charscontacts[].status"ACTIVE" | "CONVERTED" | "LOST" | "UNQUALIFIED" | "ARCHIVED"contacts[].tagsstring[]≤ 50 itemscontacts[].custom_fieldsobjectcurl -X POST "https://api.mailneo.co/api/v2/contacts/bulk" \
-H "x-api-key: $MAILNEO_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"contacts": [
{
"email": "ada@example.com",
"first_name": "Ada",
"last_name": "Lovelace",
"company": "Acme Inc",
"phone": "+1 555 0142",
"website": "https://example.com",
"linkedin": "…",
"twitter": "…",
"facebook": "…",
"source": "api",
"status": "ACTIVE",
"tags": [
"vip"
],
"custom_fields": {
"plan": "pro"
}
}
]
}'{
"data": {
"object": "bulk_result",
"requested": 1,
"created": 1,
"updated": 1,
"results": [
{
"index": 0,
"id": "c18dxuwl183y97m17tynin17j",
"email": "ada@example.com",
"operation": "created"
}
]
},
"meta": {
"request_id": "230f92cd-220f-413a-a10f-8fa7200f8e14",
"dry_run": false
}
}