> ## Documentation Index
> Fetch the complete documentation index at: https://docs.langmail.me/llms.txt
> Use this file to discover all available pages before exploring further.

# update_contact

> Change named fields of a stored contact — scalars directly, repeated fields by delta — leaving everything else untouched.

Edits a stored contact **by delta**, never by replacement. You name the fields that change; everything else keeps its stored value, so you never have to read the record and re-send it.

This is the same shape [`update_calendar_event`](/reference/mcp/update-calendar-event) uses for attendees, and for the same reason: a full-replacement object forces a caller to read the whole record first and silently drops anything it did not know to re-send. Here that would mean an agent correcting a postal code wiping a phone number a CardDAV client added an hour earlier.

Find the `contact_id` and the `[a0]`-style handles with [`search_contacts`](/reference/mcp/search-contacts) — the id appears in both response formats, the handles in `detailed`.

<Warning>
  The edit syncs to every device connected over CardDAV.
</Warning>

## Parameters

<ParamField body="contactId" type="string" required>
  The contact id, as printed by `search_contacts` under `contact_id`.
</ParamField>

<ParamField body="name" type="string">
  New display name. Omit to keep the stored one.
</ParamField>

<ParamField body="organization" type="string">
  New organization; `""` clears it. Omit to keep the stored one.
</ParamField>

<ParamField body="note" type="string">
  New note, replacing every note stored on the contact; `""` clears them. Omit to keep them. (A contact edited in a CardDAV client can carry more than one note — `search_contacts` shows them joined, and this field replaces the whole thing rather than writing beside them.)
</ParamField>

<ParamField body="addedEmails" type="object[]">
  Email addresses to add, each `{ address, context? }`. Re-adding an address already on the card is a no-op; an address that belongs to a *different* contact is rejected, the same way [`create_contact`](/reference/mcp/create-contact) rejects it.
</ParamField>

<ParamField body="removedEmails" type="string[]">
  Email addresses to remove, case-insensitive.
</ParamField>

<ParamField body="addedPhones" type="object[]">
  Phone numbers to add, each `{ number, label? }`.
</ParamField>

<ParamField body="removedPhones" type="string[]">
  Phone numbers to remove, each given either as its `[p0]` handle or as the number itself.
</ParamField>

<ParamField body="addedPostalAddresses" type="object[]">
  Postal addresses to add — same components as [`create_contact`](/reference/mcp/create-contact).
</ParamField>

<ParamField body="updatedPostalAddresses" type="object[]">
  Corrections to stored postal addresses: each entry names a `key` (the `[a0]` handle) plus only the components that change. Everything left out keeps its stored value — including parts of an imported address this API has no field for, such as a separately stored house number, apartment, or floor. Correcting `street` replaces the whole street line, which is what `search_contacts` showed you.
</ParamField>

<ParamField body="removedPostalAddresses" type="string[]">
  Postal addresses to remove, each given either as its `[a0]` handle or as its label (`home`, `work`).
</ParamField>

At least one field beyond `contactId` is required.

## Example — correcting one component

```json Call theme={null}
{
  "contactId": "c17",
  "updatedPostalAddresses": [{ "key": "a0", "postalCode": "80331" }]
}
```

```text Result theme={null}
Updated contact:

Robin Fairweather
   contact_id: c17
   organization: Northwind
   emails:
   - robin@northwind.example
   phones:
   - [p0] +49 89 1234567 (mobile)
   postal_addresses:
   - [a0] Beispielweg 1, 80331 München, DE (home)
   note: Prefers letters.
   source: authored

Fields you did not name are unchanged. The edit syncs to every device connected over CardDAV.
```

The street, city, country, organization, email, phone, and note were never sent — and are all still there.

## Errors

* `No contact with id <id>.` — the id does not resolve. Find it with `search_contacts`.
* `<address> already belongs to a different contact (contact_id: …)` — remove it there first, or edit that record instead.
* `This contact has no postal address <key>. Stored handles: …` — a correction was addressed to a handle the card does not carry. Nothing is written; read the handles back with `search_contacts` in `detailed` format.
* `update_contact needs at least one field to change.` — only `contactId` was passed.

## What this tool does not do

It does not merge two contacts, and it does not change a record's `source`. A derived record you correct stays derived — the correction is protected regardless, because collection only ever fills blanks and never overwrites a field that already has a value.
