Base URL
https://app.dmu.email/api/v1
Authentication
Bearer dmk_… API key
Rate limit
120 requests a minute per key

Update a contact

PATCH/contacts/{id}

Changes a contact. Send only what changes; null clears a field.

Needs the permissioncontacts:write

email and phone replace the first address or phone number only, while emails and phones replace the whole list.

A field you leave out is untouched, and null or an empty string clears it. Text is put on one line and trimmed. A custom text field holds up to 1,000 characters.

Custom fields by kind: a Number takes a comma as the decimal point too; a Date starts YYYY-MM-DD, and anything after is dropped; a Choice matches one of its choices, ignoring case; a Person takes a colleague’s address or user id and is returned as their address. A key your organisation doesn’t have fails the whole item.

What the API writes is locked in the app, which shows it read only: the CRM is the source of truth. Sending the value a field already has doesn’t lock it, and clearing a field with null unlocks it.

Shared and personal contacts are kept apart, so the same address can be one shared contact and one personal one.

The API may change any field, including those it set before.

visibility: "shared" shares a personal contact with the organisation. It’s one way: a shared contact can’t become personal again.

Path parameters

idstringrequired
The contact.

Body

emailstringoptional
Replaces the first address only.
emailslist of stringsoptional
Replaces every address, up to 10. Repeats are dropped, ignoring case. The first is its key.
phonestringoptional
Replaces the first phone number only.
phoneslist of stringsoptional
Replaces every phone number, up to 10, each up to 50 characters of digits, spaces and + ( ) - . / # * x.
firstNamestringoptional
Or lastName, company, jobTitle: up to 200 characters each.
fieldsobjectoptional
Custom fields by key, merged one by one: a key you leave out is untouched. Each must already exist in DMU Mail. See the notes for each kind.
visibilitystringoptional
shared shares a personal contact. Nothing makes a shared one personal again.

Returns

The contact, as List contacts describes it.

Errors

400invalid
A value is wrong, such as an address or a phone number. The error’s field says which.
400unknown_field
fields has a key your organisation doesn’t have. fields lists them.
401invalid_key
The key is wrong, expired or revoked. With no Authorization header at all, the code is missing_credentials.
403missing_scope
The key doesn’t have contacts:write. The X-Required-Scope header names what’s missing.
403personal_not_allowed
A personal contact, on a key that doesn’t include personal contacts.
404not_found
There’s no such contact, or it’s personal and the key doesn’t include personal contacts.
409duplicate
Another contact has that address. The error has its contactId.
409one_way
A shared contact can’t be made personal again.
413too_large
The request body is over 256 KB.
429rate_limited
More than 120 requests a minute from this key. Wait for Retry-After seconds.
curl https://app.dmu.email/api/v1/contacts/3d7a51c2 \
  -X PATCH \
  -H "Authorization: Bearer dmk_7f3a9c2e1b4d_Xq8v…" \
  -H "Content-Type: application/json" \
  -d '{
    "jobTitle": "Senior accountant",
    "phones": ["210 412 3380", "694 112 0457"]
  }'

Response

200 OK

{
  "data": {
    "id": "3d7a51c2",
    "visibility": "shared",
    "source": "api",
    "firstName": "Άννα",
    "lastName": "Παπαδάκη",
    "company": "Λογιστήριο Παπαδάκη",
    "jobTitle": "Senior accountant",
    "email": "anna@papadakis-acc.gr",
    "emails": ["anna@papadakis-acc.gr"],
    "phones": ["210 412 3380", "694 112 0457"],
    "fields": {
      "vat_number": "094536781",
      "customer_number": "ΠΕΛ-0412"
    },
    "locked": [
      "first_name",
      "last_name",
      "company",
      "job_title",
      "email",
      "phone",
      "vat_number",
      "customer_number"
    ],
    "createdAt": "2026-10-25T06:12:40.000Z",
    "updatedAt": "2026-10-25T06:31:05.000Z"
  }
}

Every response wraps its result in data. A page of threads adds nextCursor, which is null on the last page.