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 →

GET/contacts

List 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..100
cursorstring≤ 256 chars
Opaque page cursor from a previous response’s next_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: desc
updated_sincestring · date-time
sort"updated_at" | "created_at" | "last_activity_at"default: updated_at
status"ACTIVE" | "CONVERTED" | "LOST" | "UNQUALIFIED" | "ARCHIVED"
emailstring≤ 320 chars
sourcestring≤ 200 chars
tagsstring≤ 1000 chars
curl "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"
  }
}
POST/contacts

Create 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_runboolean
Report what this call WOULD do and commit nothing. Returns data: 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 chars
A caller-chosen unique string, at most 255 characters. Retrying with the same key replays the first response instead of repeating the work; the replay carries Idempotent-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 chars
first_namestring · nullable≤ 200 chars
last_namestring · nullable≤ 200 chars
companystring · nullable≤ 200 chars
phonestring · nullable≤ 50 chars
websitestring · nullable≤ 500 chars
linkedinstring · nullable≤ 500 chars
twitterstring · nullable≤ 500 chars
facebookstring · nullable≤ 500 chars
sourcestring · nullable≤ 200 chars
status"ACTIVE" | "CONVERTED" | "LOST" | "UNQUALIFIED" | "ARCHIVED"
tagsstring[]≤ 50 items
custom_fieldsobject
curl -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
  }
}
GET/contacts/{contactId}

Retrieve a contact

Requires contacts:read

Path parameters1
contactIdstringrequired1..64 chars
curl "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"
  }
}
PATCH/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 chars
Query parameters1
dry_runboolean
Report what this call WOULD do and commit nothing. Returns data: 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 chars
A caller-chosen unique string, at most 255 characters. Retrying with the same key replays the first response instead of repeating the work; the replay carries Idempotent-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 chars
first_namestring · nullable≤ 200 chars
last_namestring · nullable≤ 200 chars
companystring · nullable≤ 200 chars
phonestring · nullable≤ 50 chars
websitestring · nullable≤ 500 chars
linkedinstring · nullable≤ 500 chars
twitterstring · nullable≤ 500 chars
facebookstring · nullable≤ 500 chars
sourcestring · nullable≤ 200 chars
status"ACTIVE" | "CONVERTED" | "LOST" | "UNQUALIFIED" | "ARCHIVED"
tagsstring[]≤ 50 items
custom_fieldsobject
curl -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
  }
}
DELETE/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 chars
Query parameters1
dry_runboolean
Report what this call WOULD do and commit nothing. Returns data: 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
  }
}
POST/contacts/bulk

Create 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_runboolean
Report what this call WOULD do and commit nothing. Returns data: 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 chars
A caller-chosen unique string, at most 255 characters. Retrying with the same key replays the first response instead of repeating the work; the replay carries Idempotent-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 items
contacts[].emailstringrequired3..320 chars
contacts[].first_namestring · nullable≤ 200 chars
contacts[].last_namestring · nullable≤ 200 chars
contacts[].companystring · nullable≤ 200 chars
contacts[].phonestring · nullable≤ 50 chars
contacts[].websitestring · nullable≤ 500 chars
contacts[].linkedinstring · nullable≤ 500 chars
contacts[].twitterstring · nullable≤ 500 chars
contacts[].facebookstring · nullable≤ 500 chars
contacts[].sourcestring · nullable≤ 200 chars
contacts[].status"ACTIVE" | "CONVERTED" | "LOST" | "UNQUALIFIED" | "ARCHIVED"
contacts[].tagsstring[]≤ 50 items
contacts[].custom_fieldsobject
curl -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
  }
}