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

# Manage contacts

> Read and write your workspace address book through the API, and keep it in step with a CRM without creating duplicates.

[Contacts](https://docs.yungle.co/concepts.md#contact) are your workspace's address book: the people you send transfers to and invite to collections. Use the API to fill it from the system you already keep people in, such as a CRM, so they are there to pick when you send.

A contact grants nothing on its own. It is a convenience for addressing people, not a permission.

## Prerequisites

* An [API key](https://docs.yungle.co/authentication.md) with `contacts:read` and `contacts:write`, exported as `YUNGLE_API_KEY`. Contacts work on every plan, the free one included.
* Optional: `npm install yungle-client` or `pip install yungle`.

## Steps

**1. Create a contact**

`email` is the only required field. Yungle lowercases it, and it is unique per workspace. The other fields are `firstName`, `lastName`, `company`, `phone` and `type`, each up to 200 characters.

```bash curl theme={null}
curl -X POST https://yungle.co/api/v1/contacts \
  -H "Authorization: Bearer $YUNGLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Sanne",
    "lastName": "de Vries",
    "company": "De Vries BV",
    "email": "sanne@devries.example",
    "phone": "+31 20 555 0134",
    "type": "client"
  }'
```

```javascript JavaScript theme={null}
import { YungleClient } from 'yungle-client';

const yungle = new YungleClient({ apiKey: process.env.YUNGLE_API_KEY });

const { contact } = await yungle.createContact({
  firstName: 'Sanne',
  lastName: 'de Vries',
  company: 'De Vries BV',
  email: 'sanne@devries.example',
  phone: '+31 20 555 0134',
  type: 'client',
});
```

```python Python theme={null}
from yungle import Yungle

yungle = Yungle()  # reads YUNGLE_API_KEY

contact = yungle.create_contact(
    "sanne@devries.example",
    first_name="Sanne",
    last_name="de Vries",
    company="De Vries BV",
    phone="+31 20 555 0134",
    type="client",
)["contact"]
```

The response is `201`:

```json theme={null}
{
  "contact": {
    "id": "01JBD4F6H8K0M2P4R6T8V0X2Z4",
    "firstName": "Sanne",
    "lastName": "de Vries",
    "company": "De Vries BV",
    "email": "sanne@devries.example",
    "phone": "+31 20 555 0134",
    "type": "client",
    "createdAt": "2026-10-11T09:00:00.000Z",
    "updatedAt": "2026-10-11T09:00:00.000Z"
  }
}
```

An address already in the book returns `409` [`conflict`](https://docs.yungle.co/errors#conflict). A missing or invalid address, or a field over 200 characters, returns `400` [`invalid_request`](https://docs.yungle.co/errors#invalid_request) with `details.reason`.

**2. Sync from your CRM**

There is no bulk endpoint. Create contacts one at a time and treat `409 conflict` as "already there". Running the sync again then changes nothing, so it is safe to schedule.

```javascript JavaScript theme={null}
import { YungleClient, YungleApiError } from 'yungle-client';

const yungle = new YungleClient({ apiKey: process.env.YUNGLE_API_KEY });

// fromCrm: [{ first, last, company, email }, …] from your own system
for (const person of fromCrm) {
  try {
    await yungle.createContact({
      firstName: person.first,
      lastName: person.last,
      company: person.company,
      email: person.email,
      type: 'client',
    });
  } catch (err) {
    if (err instanceof YungleApiError && err.code === 'conflict') continue; // already in the book
    throw err;
  }
}
```

```python Python theme={null}
from yungle import Yungle, YungleError

yungle = Yungle()

# from_crm: [{"first": …, "last": …, "company": …, "email": …}, …]
for person in from_crm:
    try:
        yungle.create_contact(
            person["email"],
            first_name=person["first"],
            last_name=person["last"],
            company=person["company"],
            type="client",
        )
    except YungleError as err:
        if err.code == "conflict":
            continue  # already in the book
        raise
```

```bash curl theme={null}
# One request per person; a 409 means the address is already there.
status=$(curl -s -o /dev/null -w "%{http_code}" -X POST https://yungle.co/api/v1/contacts \
  -H "Authorization: Bearer $YUNGLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"Sanne","lastName":"de Vries","email":"sanne@devries.example","type":"client"}')
[ "$status" = 201 ] || [ "$status" = 409 ] || echo "failed: $status"
```

Both SDKs retry throttled requests (`429`) for you, honouring `Retry-After`. A key allows 120 requests a minute on a paid plan and 12 on the free plan; see [Limits](https://docs.yungle.co/limits.md).

To push changes for people who already exist, read the book once with `GET /contacts`, map each `email` to its `id`, and `PATCH` the ones that differ (next step).

**3. Update a contact**

`PATCH /contacts/{id}` replaces the whole contact: **any field you leave out is cleared**. Read the contact, change what you need, and send every field back.

```bash curl theme={null}
curl -X PATCH https://yungle.co/api/v1/contacts/01JBD4F6H8K0M2P4R6T8V0X2Z4 \
  -H "Authorization: Bearer $YUNGLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Sanne",
    "lastName": "de Vries",
    "company": "De Vries Studio",
    "email": "sanne@devries.example",
    "phone": "+31 20 555 0134",
    "type": "client"
  }'
```

```javascript JavaScript theme={null}
const { contact: current } = await yungle.getContact('01JBD4F6H8K0M2P4R6T8V0X2Z4');

await yungle.updateContact(current.id, {
  firstName: current.firstName,
  lastName: current.lastName,
  company: 'De Vries Studio',
  email: current.email,
  phone: current.phone,
  type: current.type,
});
```

```python Python theme={null}
current = yungle.get_contact("01JBD4F6H8K0M2P4R6T8V0X2Z4")["contact"]

yungle.update_contact(
    current["id"],
    current["email"],
    first_name=current["firstName"],
    last_name=current["lastName"],
    company="De Vries Studio",
    phone=current["phone"],
    type=current["type"],
)
```

The response is `{ "contact": { … } }`. Changing `email` to an address another contact already uses returns `409` [`conflict`](https://docs.yungle.co/errors#conflict).

**4. Delete a contact**

`DELETE /contacts/{id}` removes the contact and nothing else.

```bash curl theme={null}
curl -X DELETE https://yungle.co/api/v1/contacts/01JBD4F6H8K0M2P4R6T8V0X2Z4 \
  -H "Authorization: Bearer $YUNGLE_API_KEY"
```

```javascript JavaScript theme={null}
await yungle.deleteContact('01JBD4F6H8K0M2P4R6T8V0X2Z4');
```

```python Python theme={null}
yungle.delete_contact("01JBD4F6H8K0M2P4R6T8V0X2Z4")
```

The response is `{ "deleted": true }`.

> [!WARNING]
>
> Deleting a contact does not remove their access to anything. If they were invited to a collection as a [guest](https://docs.yungle.co/concepts.md), they keep that access. Remove it with `DELETE /collections/{id}/guests/{guestId}`.

## Contact types

`type` is one of `client`, `colleague`, `partner`, `friend`, `family` or `other`. Any other value is saved as `other` instead of failing the request.

## Reading the address book

`GET /contacts` returns every contact in one response, sorted by last name, then first name, then email. The CLI prints the same list with `yungle contacts`.

## Data protection

Contacts are personal data about people who may never have used Yungle. Keep only the people you send to.

* Contacts never expire. They stay until you delete them or delete your account, which deletes them all.
* You can get the whole book back at any time with `GET /contacts`, and it is included in your account's data export.
* Yungle does not email a contact because they are in your book. Email goes out only when you send a transfer to them or invite them to a collection.

## Next steps

> **[Send a transfer](https://docs.yungle.co/guides/send-a-transfer.md)**
>
> Email a transfer to the people in your book.

> **[Client collections](https://docs.yungle.co/guides/client-collections.md)**
>
> Invite a client to their own collection.

> **[Errors](https://docs.yungle.co/errors.md#conflict)**
>
> What `409 conflict` means and when it is safe to skip.

> **[API reference](https://docs.yungle.co/api-reference/contacts/create-a-contact.md)**
>
> Every field of the contact endpoints.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.