---
title: "nextmail.contacts"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://nextmail.sh/docs/python/reference/contacts"
area: "Python"
category: "Reference"
---

# nextmail.contacts

Every method in this namespace: its signature, its parameters, what it returns and an example.

## Methods

The workspace address book: people it has written to, plus anybody you save yourself.

### `contacts.list()`

List the workspace contacts, most recently seen first

```python
def list(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    source: ContactSource | None = None,
    q: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[ContactResource]
```

Returns one page of the workspace address book, ordered by `lastSeenAt` with the most recent first and contacts that have never been mailed last. `source` says where a row came from: `manual` for a contact somebody saved, in the app or through `create`, `auto` for an address recorded when a member sent mail to it from the app composer, and `form` for an address a sign-up form added. A saved contact stays `manual` when it is mailed later.

`source=` narrows the page to one origin, so `source='manual'` is the contacts somebody saved on purpose and `source='auto'` the ones the composer recorded. `q=` searches the name and the address.

This lists saved contacts only. `list_people` lists everyone the Contacts page in the app shows, the addresses seen in mail included, with thread counts.

Paging is keyset. `limit=` takes 1 to 200 and defaults to 50, and `nextCursor` goes back as `cursor=` while `hasMore` is `True`. Never build a cursor yourself.

Contacts belong to the whole workspace rather than to any one address, so a key limited to particular addresses or domains reads and writes the same book as every other key. An app connected by a member who reaches only some addresses is refused with 422 `capability_unsupported` on `addressAllowlist`, on every contacts, audiences and broadcasts route.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Rows per page, a whole number from 1 to 200. The server defaults to 50.
- `cursor` (`str`): The `nextCursor` from the previous page. Never build one yourself.
- `source` (`ContactSource`): `manual`, `auto` or `form`. Leave it out for the whole book.
- `q` (`str`): Searches the name and the address, up to 200 characters. When nothing matches exactly on the first page, close spellings are returned instead, and the pages that follow keep matching the same way.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[ContactResource]`, a dict with `items`, `hasMore` and `nextCursor`. Each contact has `email`, `name`, `source`, `notes`, `attributes`, `photoUrl`, `lastSeenAt`, `createdAt` and `updatedAt`.

**Example**

```python
from nextmail import nextmail

page = nextmail.contacts.list(source='manual', limit=200)

for contact in page['items']:
    print(contact['name'] or contact['email'], contact['lastSeenAt'])

print(page['hasMore'], page['nextCursor'])
```

**Notes**

- Mail sent through this API adds no contacts. Only sends from the app composer record recipients, and `create` is the way to add one deliberately.
- Every member of the workspace reads and writes the same book, so a contact saved by one person is visible to the rest.
- A contact that has never been mailed has `lastSeenAt` set to `None` and sorts last, after every contact with a date.
- Addresses are stored lower cased.

Also available in: API [`GET /contacts`](https://nextmail.sh/docs/api/reference/contacts#get-contacts); TypeScript [`nextmail.contacts.list()`](https://nextmail.sh/docs/sdk/reference/contacts#list); Ruby [`contacts.list`](https://nextmail.sh/docs/ruby/reference/contacts#list); PHP [`contacts->list`](https://nextmail.sh/docs/php/reference/contacts#list); Go [`Contacts.List`](https://nextmail.sh/docs/go/reference/contacts#list); Java [`contacts().list`](https://nextmail.sh/docs/java/reference/contacts#list); C# [`Contacts.ListAsync`](https://nextmail.sh/docs/csharp/reference/contacts#list); CLI [`nextmail contacts list`](https://nextmail.sh/docs/cli/reference/contacts#contacts-list).

### `contacts.list_all()`

Collect the whole address book into one list

```python
def list_all(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    source: ContactSource | None = None,
    q: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[ContactResource]
```

Follows `nextCursor` from page to page and returns every contact in the workspace in one list, most recently seen first and never-mailed contacts last. It takes the same `source=` and `q=` filters as `list`.

Everything is held in memory before the call returns, and an address book grows with every recipient the composer records. Prefer `iterate` when you can stop early. `limit=` sets the page size of each request, not the total.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Page size per request, from 1 to 200, defaulting to 50 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `source` (`ContactSource`): `manual`, `auto` or `form`. Leave it out for the whole book.
- `q` (`str`): Searches the name and the address, up to 200 characters. When nothing matches exactly on the first page, close spellings are returned instead, and the pages that follow keep matching the same way.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[ContactResource]` holding every contact across all pages.

**Example**

```python
from nextmail import nextmail

saved = nextmail.contacts.list_all(source='manual', limit=200)

print(len(saved), 'contacts saved on purpose')
```

**Notes**

- A failure on any page raises out of the whole call, and the contacts already fetched are discarded.

Also available in: API [`GET /contacts`](https://nextmail.sh/docs/api/reference/contacts#get-contacts); TypeScript [`nextmail.contacts.listAll()`](https://nextmail.sh/docs/sdk/reference/contacts#listAll); Ruby [`contacts.list_all`](https://nextmail.sh/docs/ruby/reference/contacts#listAll); PHP [`contacts->listAll`](https://nextmail.sh/docs/php/reference/contacts#listAll); Go [`Contacts.ListAll`](https://nextmail.sh/docs/go/reference/contacts#listAll); Java [`contacts().listAll`](https://nextmail.sh/docs/java/reference/contacts#listAll); C# [`Contacts.ListAllAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listAll).

### `contacts.iterate()`

Stream the address book one contact at a time

```python
def iterate(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    source: ContactSource | None = None,
    q: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[ContactResource]
```

Returns a generator that yields contacts one at a time, most recently seen first, and requests the next page only once the current one is used up. Nothing is fetched until the loop starts, and leaving the loop with `break` stops the requests.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Page size per request, from 1 to 200, defaulting to 50 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `source` (`ContactSource`): `manual`, `auto` or `form`. Leave it out for the whole book.
- `q` (`str`): Searches the name and the address, up to 200 characters. When nothing matches exactly on the first page, close spellings are returned instead, and the pages that follow keep matching the same way.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[ContactResource]`, a generator yielding one contact per step.

**Example**

```python
from nextmail import nextmail

for contact in nextmail.contacts.iterate(source='auto'):
    if contact['lastSeenAt'] is None:
        break

    print(contact['email'], contact['lastSeenAt'])
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.

Also available in: API [`GET /contacts`](https://nextmail.sh/docs/api/reference/contacts#get-contacts); TypeScript [`nextmail.contacts.iterate()`](https://nextmail.sh/docs/sdk/reference/contacts#iterate); Ruby [`contacts.iterate`](https://nextmail.sh/docs/ruby/reference/contacts#iterate); PHP [`contacts->iterate`](https://nextmail.sh/docs/php/reference/contacts#iterate); Go [`Contacts.Iterate`](https://nextmail.sh/docs/go/reference/contacts#iterate); Java [`contacts().iterate`](https://nextmail.sh/docs/java/reference/contacts#iterate); C# [`Contacts.IterateAsync`](https://nextmail.sh/docs/csharp/reference/contacts#iterate).

### `contacts.get()`

Read one contact by email address

```python
def get(
    email: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactDetailResource
```

Looks up a contact by address in the workspace address book. The address is trimmed and lower cased before the lookup, so `Grace@Example.com` finds `grace@example.com`, and the SDK URL encodes it for the path.

A 404, which raises `NextmailApiError` with `is_not_found`, means only that the address is not in the book. It says nothing about whether mail has been exchanged with it, since received mail never creates contacts.

Scopes: `contacts:read`.

**Parameters**

- `email` (`str`, required): The contact's address, matched case insensitively.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactDetailResource` with `email`, `name`, `source`, `notes`, `attributes`, the custom fields as a dict of keys and text values, `photoUrl` and `lastSeenAt`, plus `audiences`, every audience the contact is in as a dict with `id`, `name` and `builtin`. `builtin` is `'default'` on the audience every contact belongs to and `None` on one somebody created. A key that also holds `forms:read` gets `signUps`, what this address sent through sign-up forms, newest first and at most 20. Each is a dict with `id`, `formId`, `formName`, `status` (`'pending'` or `'added'`), `answers`, `sourceUrl`, `createdAt` and `confirmedAt`.

**Example**

```python
from nextmail import NextmailApiError, nextmail

try:
    contact = nextmail.contacts.get('grace@example.com')
    print(contact['name'], contact['notes'])
except NextmailApiError as error:
    if error.is_not_found:
        print('grace@example.com is not in the address book')
    else:
        raise
```

**Notes**

- `name` is `None` for an address recorded automatically without a display name, and `notes` is free text somebody set in the app or through `update`.
- The address is the contact's identity here and on every other contacts route. There is no id in the public contract.

Also available in: API [`GET /contacts/{email}`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-email); TypeScript [`nextmail.contacts.get()`](https://nextmail.sh/docs/sdk/reference/contacts#get); Ruby [`contacts.get`](https://nextmail.sh/docs/ruby/reference/contacts#get); PHP [`contacts->get`](https://nextmail.sh/docs/php/reference/contacts#get); Go [`Contacts.Get`](https://nextmail.sh/docs/go/reference/contacts#get); Java [`contacts().get`](https://nextmail.sh/docs/java/reference/contacts#get); C# [`Contacts.GetAsync`](https://nextmail.sh/docs/csharp/reference/contacts#get); CLI [`nextmail contacts get`](https://nextmail.sh/docs/cli/reference/contacts#contacts-get).

### `contacts.create()`

Save a contact in the workspace address book

```python
def create(
    body: ContactCreate,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactDetailResource
```

Adds one address to the workspace address book and returns the saved row. `email` is trimmed and lower cased before it is stored, so `Grace@Example.com` and `grace@example.com` are the same contact.

The contact is saved with `source` set to `manual`, the same value a contact typed into the app carries, and it joins the default audience as it is written. Every contact is in that audience for as long as it exists, so there is nothing to add afterwards. Name audiences of your own in `audienceIds` to put it in them in the same call, or add it later with `audiences.add_contact`. Sending `audienceIds` also requires the `audiences:write` scope, since it writes memberships as well as a contact.

An address can be in the book once. A second create for an address that is already there is refused with 409 `contact_exists` rather than merged, so a retry cannot quietly overwrite a name somebody edited in the app. Read the existing row with `get` and change it with `update`, or use `save`, which accepts either.

Scopes: `contacts:write`.

**Parameters**

- `body['email']` (`str`, required): The address to save, trimmed and lower cased before it is stored.
- `body['name']` (`str | None`): Display name. Leave it out to save the contact without one.
- `body['notes']` (`str | None`): Free text kept with the contact and shown beside it in the app.
- `body['attributes']` (`dict[str, str | None]`): Custom fields to set, as a dict of keys and text values such as `{'plan': 'team'}`. A key starts with a letter and holds up to 64 letters, digits and underscores, and a value is text of up to 500 characters, trimmed before it is stored. `None` or an empty string removes a field, a field you leave out keeps its value, and a contact holds at most 50.
- `body['audienceIds']` (`list[str]`): Up to 25 audience ids to put the new contact in. The default audience is joined whether or not it is named. When this is present the key also needs `audiences:write`, or the call is refused with 403 before anything is saved.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactDetailResource` with `email`, `name`, `source` as `manual`, `notes`, `attributes` and `lastSeenAt`, which is `None` until mail goes to the address from the app composer, plus `audiences`, every audience the contact is now in, the default one included.

**Example**

```python
from nextmail import nextmail

contact = nextmail.contacts.create(
    {
        'email': 'grace@example.com',
        'name': 'Grace Hopper',
        'notes': 'Met at the compiler workshop',
        'attributes': {'plan': 'team'},
    }
)

print(contact['email'], contact['source'], contact['attributes'])
```

**Notes**

- The SDK does not retry a create after a network failure. If you retry by hand after a lost response, a 409 `contact_exists` means the first attempt landed, and `is_conflict` on the error says so.
- Contacts belong to the workspace, so the contact this creates is the one every member and every other key sees.
- An id in `audienceIds` that is not an audience of this workspace fails the whole create. Nothing is saved.
- A custom field that breaks a rule is 422 `invalid_contact`, with `param` set to `attributes.<key>` for a bad key or a value over 500 characters and to `attributes` for a 51st field. Nothing is written.

Also available in: API [`POST /contacts`](https://nextmail.sh/docs/api/reference/contacts#post-contacts); TypeScript [`nextmail.contacts.create()`](https://nextmail.sh/docs/sdk/reference/contacts#create); Ruby [`contacts.create`](https://nextmail.sh/docs/ruby/reference/contacts#create); PHP [`contacts->create`](https://nextmail.sh/docs/php/reference/contacts#create); Go [`Contacts.Create`](https://nextmail.sh/docs/go/reference/contacts#create); Java [`contacts().create`](https://nextmail.sh/docs/java/reference/contacts#create); C# [`Contacts.CreateAsync`](https://nextmail.sh/docs/csharp/reference/contacts#create); CLI [`nextmail contacts create`](https://nextmail.sh/docs/cli/reference/contacts#contacts-create).

### `contacts.update()`

Change a contact's name, notes or custom fields

```python
def update(
    email: str,
    patch: ContactPatch,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactDetailResource
```

Changes the fields you send and leaves the rest alone. A key you leave out keeps its stored value, and an explicit `None` clears it, so `{'notes': None}` empties the notes while `{}` changes nothing. `attributes` is merged key by key: a string sets a custom field, `None` removes one and the fields you do not name are kept.

The address cannot be changed. It is the contact's identity, its path segment and the unique key of the book, so moving a contact to a new address is a `delete` and a `create`, and that new contact starts with no audience membership beyond the default one.

`source` and `lastSeenAt` are the server's to set and are not accepted here. A contact recorded automatically stays `auto` after you give it a name.

Scopes: `contacts:write`.

**Parameters**

- `email` (`str`, required): The contact's address, matched case insensitively.
- `patch['name']` (`str | None`): New display name. `None` clears it.
- `patch['notes']` (`str | None`): New notes. `None` clears them.
- `patch['attributes']` (`dict[str, str | None]`): Custom fields to set, as a dict of keys and text values such as `{'plan': 'team'}`. A key starts with a letter and holds up to 64 letters, digits and underscores, and a value is text of up to 500 characters, trimmed before it is stored. `None` or an empty string removes a field, a field you leave out keeps its value, and a contact holds at most 50.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactDetailResource` as it stands after the change, with `audiences` alongside it.

**Example**

```python
from nextmail import nextmail

contact = nextmail.contacts.update(
    'grace@example.com',
    {'name': 'Grace Hopper', 'notes': None, 'attributes': {'plan': 'team', 'trial': None}},
)

print(contact['name'], contact['notes'], contact['attributes'])
```

**Notes**

- The SDK retries this call after a network failure, since the same patch sent twice leaves the same contact.
- An address that is not in the book is a 404, the same as `get`.
- A custom field that breaks a rule is 422 `invalid_contact`, with `param` set to `attributes.<key>` for a bad key or a value over 500 characters and to `attributes` for a 51st field. Nothing is written.
- Audience membership is not touched here. Use `set_audiences` to set the whole list, or `audiences.add_contact` and `audiences.remove_contact` for one audience.

Also available in: API [`PATCH /contacts/{email}`](https://nextmail.sh/docs/api/reference/contacts#patch-contacts-email); TypeScript [`nextmail.contacts.update()`](https://nextmail.sh/docs/sdk/reference/contacts#update); Ruby [`contacts.update`](https://nextmail.sh/docs/ruby/reference/contacts#update); PHP [`contacts->update`](https://nextmail.sh/docs/php/reference/contacts#update); Go [`Contacts.Update`](https://nextmail.sh/docs/go/reference/contacts#update); Java [`contacts().update`](https://nextmail.sh/docs/java/reference/contacts#update); C# [`Contacts.UpdateAsync`](https://nextmail.sh/docs/csharp/reference/contacts#update); CLI [`nextmail contacts update`](https://nextmail.sh/docs/cli/reference/contacts#contacts-update).

### `contacts.delete()`

Delete somebody from the contacts and hide the address

```python
def delete(
    email: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> DeletedContactResource
```

Does what Delete does on the Contacts page in the app. A saved contact goes with its notes, its photo and every audience it was in, the default one included. The address is then hidden: `list_people` leaves it out, and mail sent to it from the app composer no longer records it as a contact. The address can be one that was only ever seen in mail, which is how you take somebody off the people list, and `wasSaved` says which it was.

Nothing else moves: the mail exchanged with the address stays in the mailbox, and the address can still be written to. There is no undo. `create` or `save` afterwards brings the address back as a new contact with no name, no notes and no membership beyond the default audience.

Scopes: `contacts:write`.

**Parameters**

- `email` (`str`, required): The contact's address, matched case insensitively.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`DeletedContactResource` with `object` set to `'contact'`, `email`, `deleted` set to `True` and `wasSaved`, which is `True` when the address was a saved contact.

**Example**

```python
from nextmail import nextmail

removed = nextmail.contacts.delete('grace@example.com')

print(removed['email'], 'was saved' if removed['wasSaved'] else 'was only seen in mail')
```

**Notes**

- An address that is not in the book is no longer a 404: it is hidden and answers with `wasSaved` set to `False`. Something that is not an address is a 422 `invalid_contact`.
- The SDK does not retry a delete, though a second attempt is harmless: it answers with `wasSaved` set to `False`.
- `delete_many` deletes up to 200 addresses in one call.

Also available in: API [`DELETE /contacts/{email}`](https://nextmail.sh/docs/api/reference/contacts#delete-contacts-email); TypeScript [`nextmail.contacts.delete()`](https://nextmail.sh/docs/sdk/reference/contacts#delete); Ruby [`contacts.delete`](https://nextmail.sh/docs/ruby/reference/contacts#delete); PHP [`contacts->delete`](https://nextmail.sh/docs/php/reference/contacts#delete); Go [`Contacts.Delete`](https://nextmail.sh/docs/go/reference/contacts#delete); Java [`contacts().delete`](https://nextmail.sh/docs/java/reference/contacts#delete); C# [`Contacts.DeleteAsync`](https://nextmail.sh/docs/csharp/reference/contacts#delete); CLI [`nextmail contacts delete`](https://nextmail.sh/docs/cli/reference/contacts#contacts-delete).

### `contacts.set_audiences()`

Set exactly which audiences a contact is in

```python
def set_audiences(
    email: str,
    body: ContactAudiencesSet,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactDetailResource
```

Makes the contact's audiences match the list you send, the way the audience picker on a contact does in the app. The contact joins every listed audience it is not in yet and leaves every other one, in one transaction, and memberships it keeps keep their `addedAt`.

The default audience is always kept, whether or not you name it, so `{'audienceIds': []}` leaves the contact in the default audience alone. To add or remove one audience without restating the rest, use `audiences.add_contact` or `audiences.remove_contact`.

The address is trimmed and lower cased, the SDK URL encodes it for the path, and it has to be a contact already: an address that is not in the book is a 404 `contact_not_found` on `email`. Create it with `create`, which takes `audienceIds` too. An id that is not an audience of this workspace is a 404 `audience_not_found` on `audienceIds`, and nothing is changed.

Scopes: `audiences:write`.

**Parameters**

- `email` (`str`, required): The contact's address, matched case insensitively.
- `body['audienceIds']` (`list[str]`, required): Every audience the contact should be in, up to 100 ids. A repeated id counts once. More than 100 is a 422 `invalid_parameter` on `audienceIds`, and an empty id is the same error on that entry, such as `audienceIds.0`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactDetailResource` as it stands after the change, with `audiences`, every audience the contact is now in as a dict with `id`, `name` and `builtin`, the default one included.

**Example**

```python
from nextmail import nextmail

contact = nextmail.contacts.set_audiences(
    'grace@example.com',
    {'audienceIds': ['aud_9f2c4b7e1a0d63d84c5f2e7b', 'aud_1c4e7a9b2d0f36e85a7c1b4d']},
)

print([audience['name'] for audience in contact['audiences']])
```

**Notes**

- `audiences:write` is the only scope checked, because this writes memberships rather than the contact.
- Safe to replay: sending the same list again changes nothing. The SDK retries it after a network failure.
- Read the current list with `get` first when you mean to add one audience to what the contact already has, or use `audiences.add_contact`.

Also available in: API [`PUT /contacts/{email}/audiences`](https://nextmail.sh/docs/api/reference/contacts#put-contacts-email-audiences); TypeScript [`nextmail.contacts.setAudiences()`](https://nextmail.sh/docs/sdk/reference/contacts#setAudiences); Ruby [`contacts.set_audiences`](https://nextmail.sh/docs/ruby/reference/contacts#setAudiences); PHP [`contacts->setAudiences`](https://nextmail.sh/docs/php/reference/contacts#setAudiences); Go [`Contacts.SetAudiences`](https://nextmail.sh/docs/go/reference/contacts#setAudiences); Java [`contacts().setAudiences`](https://nextmail.sh/docs/java/reference/contacts#setAudiences); C# [`Contacts.SetAudiencesAsync`](https://nextmail.sh/docs/csharp/reference/contacts#setAudiences); CLI [`nextmail contacts set-audiences`](https://nextmail.sh/docs/cli/reference/contacts#contacts-set-audiences).

### `contacts.list_people()`

List everyone on the Contacts page, saved or seen in mail

```python
def list_people(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    q: str | None = None,
    email: str | None = None,
    sort: PeopleSort | None = None,
    blocked: bool | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> PeoplePage
```

Returns one page of the people the Contacts page in the app lists: the saved contacts, and every address seen in mail as the sender or a recipient of a thread's newest message. Each row says whether it is `saved`, how many `threads` it shares with the mailbox and when mail last moved (`lastAt`). A person seen in mail and saved is one row. `list` is the saved contacts alone.

The addresses seen in mail are included only when the key also holds `threads:read`, because they are read out of the mail. Without it the page holds the saved contacts and `seen` is `False`. Deleted addresses and the mailbox's own addresses are never listed.

`sort=` is `recent`, newest mail first and saved contacts never seen in mail after them, `name`, by name or else by address, ignoring case, or `threads`, most threads first. `blockedBy` on each row names the workspace blocklist rule that blocks it, and `blocked=True` narrows the page to those rows.

Paging is keyset. `limit=` takes 1 to 100 and defaults to 25, and `nextCursor` goes back as `cursor=`, with the same `sort=`, `q=` and `blocked=`, while `hasMore` is `True`.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Rows per page, 1 to 100. The server defaults to 25.
- `cursor` (`str`): The `nextCursor` from the previous page. Never build one yourself.
- `q` (`str`): Searches names, addresses and notes, up to 200 characters. When nothing matches exactly on the first page, close spellings are returned instead.
- `email` (`str`): One address only, matched case insensitively: the way to read one person's thread count and last mail.
- `sort` (`PeopleSort`): `recent` (the default), `name` or `threads`.
- `blocked` (`bool`): `True` for only the people the workspace blocklist blocks, whole-domain rules included.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`PeoplePage`, a dict with `items`, `hasMore`, `nextCursor` and `seen`. Each `PersonResource` has `email`, `displayEmail`, `name`, `saved`, `source`, `notes`, `attributes`, which is empty for an address only seen in mail, `photoUrl`, `threads`, `lastAt`, `createdAt`, `updatedAt` and `blockedBy`.

**Example**

```python
from nextmail import nextmail

page = nextmail.contacts.list_people(sort='threads', limit=50)

for person in page['items']:
    kind = 'saved' if person['saved'] else 'seen in mail'
    print(person['name'] or person['email'], person['threads'], kind)

print('Addresses seen in mail included:', page['seen'])
```

**Notes**

- `email` is lower cased and is what every other contacts method takes. `displayEmail` keeps the capitals the newest message wrote.
- A malformed or stale cursor is a 400 `invalid_cursor`. Start again without one.
- A key limited to particular addresses or domains gets the saved contacts alone, with `seen` set to `False`, even when it holds `threads:read`, because the addresses seen in mail would be read from the mail of every address in the workspace.

Also available in: API [`GET /contacts/people`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-people); TypeScript [`nextmail.contacts.listPeople()`](https://nextmail.sh/docs/sdk/reference/contacts#listPeople); Ruby [`contacts.list_people`](https://nextmail.sh/docs/ruby/reference/contacts#listPeople); PHP [`contacts->listPeople`](https://nextmail.sh/docs/php/reference/contacts#listPeople); Go [`Contacts.ListPeople`](https://nextmail.sh/docs/go/reference/contacts#listPeople); Java [`contacts().listPeople`](https://nextmail.sh/docs/java/reference/contacts#listPeople); C# [`Contacts.ListPeopleAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listPeople); CLI [`nextmail contacts list-people`](https://nextmail.sh/docs/cli/reference/contacts#contacts-list-people).

### `contacts.list_all_people()`

Collect everyone on the Contacts page into one list

```python
def list_all_people(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    q: str | None = None,
    email: str | None = None,
    sort: PeopleSort | None = None,
    blocked: bool | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[PersonResource]
```

Follows `nextCursor` from page to page and returns every person `list_people` would list in one list, in the same order and with the same filters. It is how you count the people a blocklist blocks: `len(nextmail.contacts.list_all_people(blocked=True))`.

Everything is held in memory before the call returns, and a busy mailbox has seen a great many addresses. Prefer `iterate_people` when you can stop early.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Page size per request, from 1 to 100, defaulting to 25 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `q` (`str`): Searches names, addresses and notes, up to 200 characters. When nothing matches exactly on the first page, close spellings are returned instead.
- `email` (`str`): One address only, matched case insensitively: the way to read one person's thread count and last mail.
- `sort` (`PeopleSort`): `recent` (the default), `name` or `threads`.
- `blocked` (`bool`): `True` for only the people the workspace blocklist blocks, whole-domain rules included.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[PersonResource]` holding every person across all pages.

**Example**

```python
from nextmail import nextmail

blocked = nextmail.contacts.list_all_people(blocked=True, limit=100)

print(len(blocked), 'people are blocked')
```

**Notes**

- A failure on any page raises out of the whole call, and the people already fetched are discarded.
- `seen` is not reported here. Read one page with `list_people` to learn whether the key reads the people seen in mail.

Also available in: API [`GET /contacts/people`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-people); TypeScript [`nextmail.contacts.listAllPeople()`](https://nextmail.sh/docs/sdk/reference/contacts#listAllPeople); Ruby [`contacts.list_all_people`](https://nextmail.sh/docs/ruby/reference/contacts#listAllPeople); PHP [`contacts->listAllPeople`](https://nextmail.sh/docs/php/reference/contacts#listAllPeople); Go [`Contacts.ListAllPeople`](https://nextmail.sh/docs/go/reference/contacts#listAllPeople); Java [`contacts().listAllPeople`](https://nextmail.sh/docs/java/reference/contacts#listAllPeople); C# [`Contacts.ListAllPeopleAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listAllPeople).

### `contacts.iterate_people()`

Stream everyone on the Contacts page one person at a time

```python
def iterate_people(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    q: str | None = None,
    email: str | None = None,
    sort: PeopleSort | None = None,
    blocked: bool | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[PersonResource]
```

Returns a generator that yields the people `list_people` lists, one at a time, and requests the next page only once the current one is used up. Nothing is fetched until the loop starts, and leaving the loop with `break` stops the requests.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Page size per request, from 1 to 100, defaulting to 25 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `q` (`str`): Searches names, addresses and notes, up to 200 characters. When nothing matches exactly on the first page, close spellings are returned instead.
- `email` (`str`): One address only, matched case insensitively: the way to read one person's thread count and last mail.
- `sort` (`PeopleSort`): `recent` (the default), `name` or `threads`.
- `blocked` (`bool`): `True` for only the people the workspace blocklist blocks, whole-domain rules included.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[PersonResource]`, a generator yielding one person per step.

**Example**

```python
from nextmail import nextmail

for person in nextmail.contacts.iterate_people(sort='recent'):
    if not person['saved'] and (person['threads'] or 0) > 5:
        nextmail.contacts.save(person['email'])
        print('Saved', person['email'])
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.

Also available in: API [`GET /contacts/people`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-people); TypeScript [`nextmail.contacts.iteratePeople()`](https://nextmail.sh/docs/sdk/reference/contacts#iteratePeople); Ruby [`contacts.iterate_people`](https://nextmail.sh/docs/ruby/reference/contacts#iteratePeople); PHP [`contacts->iteratePeople`](https://nextmail.sh/docs/php/reference/contacts#iteratePeople); Go [`Contacts.IteratePeople`](https://nextmail.sh/docs/go/reference/contacts#iteratePeople); Java [`contacts().iteratePeople`](https://nextmail.sh/docs/java/reference/contacts#iteratePeople); C# [`Contacts.IteratePeopleAsync`](https://nextmail.sh/docs/csharp/reference/contacts#iteratePeople).

### `contacts.save()`

Save an address as a contact, or keep one that was recorded

```python
def save(
    email: str,
    body: ContactSave | None = None,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactDetailResource
```

Does what Add to contacts and Keep in contacts do in the app, and is safe to call whatever state the address is in. An address that is not a contact yet becomes one with `source` `manual`. A contact recorded from a send becomes `manual`. A contact already saved keeps what it has. A deleted address is brought back.

`body['name']` replaces the stored name and leaving it out keeps it. `body['notes']` replaces the stored notes and `None` clears them. `body['attributes']` sets the custom fields it names and keeps the rest. Unlike `create`, an address already in the book is not an error, and unlike `update`, an address not in the book is not one either. `body` is optional, so `save(email)` keeps whatever the contact already has.

Scopes: `contacts:write`.

**Parameters**

- `email` (`str`, required): The address, trimmed and lower cased on the server.
- `body['name']` (`str`): Up to 200 characters. Left out, the stored name is kept.
- `body['notes']` (`str | None`): Up to 5,000 characters. `None` clears the notes, and left out, they are kept.
- `body['attributes']` (`dict[str, str | None]`): Custom fields to set, as a dict of keys and text values such as `{'plan': 'team'}`. A key starts with a letter and holds up to 64 letters, digits and underscores, and a value is text of up to 500 characters, trimmed before it is stored. `None` or an empty string removes a field, a field you leave out keeps its value, and a contact holds at most 50.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactDetailResource`, the contact as it stands after the save, with `audiences`.

**Example**

```python
from nextmail import nextmail

contact = nextmail.contacts.save('grace@example.com', {'name': 'Grace Hopper'})

print(contact['email'], contact['source'])
```

**Notes**

- The server answers 201 for a new contact and 200 for one that was there. The method returns the contact either way, so compare `createdAt` with `updatedAt` if you need to tell them apart.
- Safe to replay, so the SDK retries it after a network failure.
- A new contact joins the default audience, as it does from `create`.

Also available in: API [`PUT /contacts/{email}`](https://nextmail.sh/docs/api/reference/contacts#put-contacts-email); TypeScript [`nextmail.contacts.save()`](https://nextmail.sh/docs/sdk/reference/contacts#save); Ruby [`contacts.save`](https://nextmail.sh/docs/ruby/reference/contacts#save); PHP [`contacts->save`](https://nextmail.sh/docs/php/reference/contacts#save); Go [`Contacts.Save`](https://nextmail.sh/docs/go/reference/contacts#save); Java [`contacts().save`](https://nextmail.sh/docs/java/reference/contacts#save); C# [`Contacts.SaveAsync`](https://nextmail.sh/docs/csharp/reference/contacts#save); CLI [`nextmail contacts save`](https://nextmail.sh/docs/cli/reference/contacts#contacts-save).

### `contacts.delete_many()`

Delete up to 200 contacts in one call

```python
def delete_many(
    emails: Sequence[str],
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactBatchDeleteResource
```

Deletes every address in `emails` the way `delete` deletes one: a saved contact goes with its notes, its photo and every audience membership, and every address is hidden, so mail sent to it from the app composer does not record it again. Addresses that were only seen in mail are hidden too.

An entry that is not an address comes back in `invalid` and the rest still go through. A repeated address counts once. There is no undo.

Scopes: `contacts:write`.

**Parameters**

- `emails` (`Sequence[str]`, required): A list of 1 to 200 addresses, matched case insensitively. More than 200, or none, is a 422 on `emails`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactBatchDeleteResource` with `object` set to `'contact_batch_delete'`, `deleted`, `saved` and `invalid`. `deleted` counts the addresses deleted and hidden, `saved` how many of them were saved contacts, and `invalid` lists the entries that were not addresses.

**Example**

```python
from nextmail import nextmail

result = nextmail.contacts.delete_many(['ada@example.com', 'grace@example.com'])

print(result['deleted'], 'deleted,', result['saved'], 'of them saved contacts')
```

**Notes**

- Safe to replay: deleting an address twice leaves it deleted and hidden, so the SDK retries it after a network failure.
- No mail is deleted, and nobody is unsubscribed from anything.

Also available in: API [`POST /contacts/batch-delete`](https://nextmail.sh/docs/api/reference/contacts#post-contacts-batch-delete); TypeScript [`nextmail.contacts.deleteMany()`](https://nextmail.sh/docs/sdk/reference/contacts#deleteMany); Ruby [`contacts.delete_many`](https://nextmail.sh/docs/ruby/reference/contacts#deleteMany); PHP [`contacts->deleteMany`](https://nextmail.sh/docs/php/reference/contacts#deleteMany); Go [`Contacts.DeleteMany`](https://nextmail.sh/docs/go/reference/contacts#deleteMany); Java [`contacts().deleteMany`](https://nextmail.sh/docs/java/reference/contacts#deleteMany); C# [`Contacts.DeleteManyAsync`](https://nextmail.sh/docs/csharp/reference/contacts#deleteMany); CLI [`nextmail contacts delete-many`](https://nextmail.sh/docs/cli/reference/contacts#contacts-delete-many).

### `contacts.set_photo()`

Upload the photo shown for a contact

```python
def set_photo(
    email: str,
    data: RawBody,
    *,
    content_type: ContactPhotoType | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactDetailResource
```

Sends the image bytes as the request body, replacing any photo the contact had. PNG, JPEG, WebP and GIF are accepted, up to 5 MB. The server fits the image into a 512 pixel square, stores it as WebP, keeps the first frame of an animation, and answers with the contact and its new `photoUrl`.

`data` is `bytes`, a `bytearray` or a `memoryview`. The type is read from `content_type=`, and without it the bytes go as `application/octet-stream`, which the server refuses with 422 `invalid_image`, so always pass it.

The address has to be a saved contact already: `save` it first.

Scopes: `contacts:write`.

**Parameters**

- `email` (`str`, required): The contact's address, matched case insensitively.
- `data` (`RawBody`, required): The image as `bytes`, a `bytearray` or a `memoryview`.
- `content_type` (`ContactPhotoType`): `image/png`, `image/jpeg`, `image/webp` or `image/gif`. Left out, the upload is refused with 422 `invalid_image`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactDetailResource` with the new `photoUrl`.

**Example**

```python
from pathlib import Path

from nextmail import nextmail

contact = nextmail.contacts.set_photo(
    'grace@example.com', Path('photo.jpg').read_bytes(), content_type='image/jpeg'
)

print(contact['photoUrl'])
```

**Notes**

- An address that is not a saved contact is a 404 `contact_not_found`.
- A busy image service answers 503 `image_busy`, which the SDK retries like any other 503.
- Every upload gets a new URL, so a cached old photo never shows under the new one.

Also available in: API [`PUT /contacts/{email}/photo`](https://nextmail.sh/docs/api/reference/contacts#put-contacts-email-photo); TypeScript [`nextmail.contacts.setPhoto()`](https://nextmail.sh/docs/sdk/reference/contacts#setPhoto); Ruby [`contacts.set_photo`](https://nextmail.sh/docs/ruby/reference/contacts#setPhoto); PHP [`contacts->setPhoto`](https://nextmail.sh/docs/php/reference/contacts#setPhoto); Go [`Contacts.SetPhoto`](https://nextmail.sh/docs/go/reference/contacts#setPhoto); Java [`contacts().setPhoto`](https://nextmail.sh/docs/java/reference/contacts#setPhoto); C# [`Contacts.SetPhotoAsync`](https://nextmail.sh/docs/csharp/reference/contacts#setPhoto); CLI [`nextmail contacts set-photo`](https://nextmail.sh/docs/cli/reference/contacts#contacts-set-photo).

### `contacts.remove_photo()`

Remove a contact photo

```python
def remove_photo(
    email: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactDetailResource
```

Takes the photo off the contact and deletes the stored image, the way Remove does on a contact in the app. The call returns the contact with `photoUrl` set to `None`. Removing a photo from a contact that has none changes nothing.

Scopes: `contacts:write`.

**Parameters**

- `email` (`str`, required): The contact's address, matched case insensitively.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactDetailResource` with `photoUrl` set to `None`.

**Example**

```python
from nextmail import nextmail

contact = nextmail.contacts.remove_photo('grace@example.com')

print(contact['email'], contact['photoUrl'])
```

**Notes**

- An address that is not a saved contact is a 404 `contact_not_found`.
- Safe to replay, so the SDK retries it after a network failure.

Also available in: API [`DELETE /contacts/{email}/photo`](https://nextmail.sh/docs/api/reference/contacts#delete-contacts-email-photo); TypeScript [`nextmail.contacts.removePhoto()`](https://nextmail.sh/docs/sdk/reference/contacts#removePhoto); Ruby [`contacts.remove_photo`](https://nextmail.sh/docs/ruby/reference/contacts#removePhoto); PHP [`contacts->removePhoto`](https://nextmail.sh/docs/php/reference/contacts#removePhoto); Go [`Contacts.RemovePhoto`](https://nextmail.sh/docs/go/reference/contacts#removePhoto); Java [`contacts().removePhoto`](https://nextmail.sh/docs/java/reference/contacts#removePhoto); C# [`Contacts.RemovePhotoAsync`](https://nextmail.sh/docs/csharp/reference/contacts#removePhoto); CLI [`nextmail contacts remove-photo`](https://nextmail.sh/docs/cli/reference/contacts#contacts-remove-photo).

### `contacts.block()`

Block an address

```python
def block(
    email: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactBlockResource
```

Puts the address on the workspace blocklist, the same list `settings.update` edits as `blockedSenders`, so mail from it is refused from then on. This is Block on a contact in the app. A plus tag is dropped: blocking `ada+news@example.com` blocks `ada@example.com`, and every tag of it.

When a rule already blocks the address, a whole-domain rule included, nothing is added: `created` is `False` and `blockedBy` names that rule. The address does not have to be a contact.

Scopes: `settings:write`.

**Parameters**

- `email` (`str`, required): The address to block.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactBlockResource` with `object` set to `'contact_block'`, `email`, `blocked` set to `True`, `blockedBy`, the rule that blocks it as a dict with `rule` and `list`, and `created`. Both are optional keys of the type, which `unblock` shares, so read them with `.get()`.

**Example**

```python
from nextmail import nextmail

result = nextmail.contacts.block('spammer@example.com')
rule = result.get('blockedBy')

if result.get('created'):
    print('Blocked', result['email'])
elif rule is not None:
    print('Already blocked by', rule['rule'], 'in', rule['list'])
```

**Notes**

- It needs `settings:write` rather than `contacts:write`, because it writes the blocklist rather than the contact.
- Safe to replay, so the SDK retries it after a network failure.
- An address with fewer than two letters or numbers is refused with 422 `blocklist_entry_too_broad`.
- A key limited to particular addresses or domains gets 422 `capability_unsupported` on `addressAllowlist`, because the blocklist belongs to the whole workspace and filters the mail of every address in it.

Also available in: API [`POST /contacts/{email}/block`](https://nextmail.sh/docs/api/reference/contacts#post-contacts-email-block); TypeScript [`nextmail.contacts.block()`](https://nextmail.sh/docs/sdk/reference/contacts#block); Ruby [`contacts.block`](https://nextmail.sh/docs/ruby/reference/contacts#block); PHP [`contacts->block`](https://nextmail.sh/docs/php/reference/contacts#block); Go [`Contacts.Block`](https://nextmail.sh/docs/go/reference/contacts#block); Java [`contacts().block`](https://nextmail.sh/docs/java/reference/contacts#block); C# [`Contacts.BlockAsync`](https://nextmail.sh/docs/csharp/reference/contacts#block); CLI [`nextmail contacts block`](https://nextmail.sh/docs/cli/reference/contacts#contacts-block).

### `contacts.unblock()`

Unblock an address

```python
def unblock(
    email: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactBlockResource
```

Takes every workspace blocklist rule that blocks the address off the list and lists them in `removed`. This is Unblock on a contact in the app.

When one of them is a whole-domain rule, in `blockedDomains`, everybody at that domain is unblocked with it, so check `removed` when that matters. Rules set for one address or one domain in the settings are not touched. An address that no rule blocks answers with `removed` empty.

Scopes: `settings:write`.

**Parameters**

- `email` (`str`, required): The address to unblock.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactBlockResource` with `object` set to `'contact_block'`, `email`, `blocked` set to `False` and `removed`, a list of dicts with `rule` and `list`. It is an optional key of the type, which `block` shares, so read it with `.get()`.

**Example**

```python
from nextmail import nextmail

result = nextmail.contacts.unblock('friend@example.com')

for removed in result.get('removed', []):
    print('Removed', removed['rule'], 'from', removed['list'])
```

**Notes**

- Safe to replay, so the SDK retries it after a network failure.
- A key limited to particular addresses or domains gets 422 `capability_unsupported` on `addressAllowlist`, because the blocklist belongs to the whole workspace and filters the mail of every address in it.

Also available in: API [`DELETE /contacts/{email}/block`](https://nextmail.sh/docs/api/reference/contacts#delete-contacts-email-block); TypeScript [`nextmail.contacts.unblock()`](https://nextmail.sh/docs/sdk/reference/contacts#unblock); Ruby [`contacts.unblock`](https://nextmail.sh/docs/ruby/reference/contacts#unblock); PHP [`contacts->unblock`](https://nextmail.sh/docs/php/reference/contacts#unblock); Go [`Contacts.Unblock`](https://nextmail.sh/docs/go/reference/contacts#unblock); Java [`contacts().unblock`](https://nextmail.sh/docs/java/reference/contacts#unblock); C# [`Contacts.UnblockAsync`](https://nextmail.sh/docs/csharp/reference/contacts#unblock); CLI [`nextmail contacts unblock`](https://nextmail.sh/docs/cli/reference/contacts#contacts-unblock).

### `contacts.list_threads()`

List the conversations with one person

```python
def list_threads(
    email: str,
    *,
    limit: int | None = None,
    cursor: str | None = None,
    q: str | None = None,
    sort: ContactThreadSort | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[ContactThreadResource]
```

Returns one page of the threads the address wrote or was written to, in every folder: the Mail tab on a contact in the app. Each row is a summary, `subject`, `from`, `receivedAt`, `messageCount`, `hasUnread` and `labels`, and `threads.get` reads the messages behind its `id`.

`limit=` takes 1 to 100 and defaults to 25. Pass `nextCursor` back as `cursor=`, with the same `q=` and `sort=`, while `hasMore` is `True`.

Scopes: `threads:read`.

**Parameters**

- `email` (`str`, required): The address. It does not have to be a saved contact.
- `limit` (`int`): Threads per page, 1 to 100. The server defaults to 25.
- `cursor` (`str`): The `nextCursor` from the previous page. Never build one yourself.
- `q` (`str`): Searches inside those threads, with the mailbox search syntax, up to 200 characters.
- `sort` (`ContactThreadSort`): `newest` (the default), `oldest`, `sender` or `subject`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[ContactThreadResource]`, a dict with `items`, `hasMore` and `nextCursor`.

**Example**

```python
from nextmail import nextmail

page = nextmail.contacts.list_threads('ada@example.com', q='invoice')

for thread in page['items']:
    print(thread['receivedAt'], thread['subject'], thread['messageCount'])
```

**Notes**

- It needs `threads:read`, because it reads mail rather than the contact.
- A key limited to particular addresses or domains gets 422 `capability_unsupported` on `addressAllowlist`, because a contact's threads and activity are read from the mail of every address in the workspace.

Also available in: API [`GET /contacts/{email}/threads`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-email-threads); TypeScript [`nextmail.contacts.listThreads()`](https://nextmail.sh/docs/sdk/reference/contacts#listThreads); Ruby [`contacts.list_threads`](https://nextmail.sh/docs/ruby/reference/contacts#listThreads); PHP [`contacts->listThreads`](https://nextmail.sh/docs/php/reference/contacts#listThreads); Go [`Contacts.ListThreads`](https://nextmail.sh/docs/go/reference/contacts#listThreads); Java [`contacts().listThreads`](https://nextmail.sh/docs/java/reference/contacts#listThreads); C# [`Contacts.ListThreadsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listThreads); CLI [`nextmail contacts list-threads`](https://nextmail.sh/docs/cli/reference/contacts#contacts-list-threads).

### `contacts.list_all_threads()`

Collect every conversation with one person into one list

```python
def list_all_threads(
    email: str,
    *,
    limit: int | None = None,
    cursor: str | None = None,
    q: str | None = None,
    sort: ContactThreadSort | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[ContactThreadResource]
```

Follows `nextCursor` from page to page and returns every thread `list_threads` would list for the address in one list, in the same order.

Everything is held in memory before the call returns. Prefer `iterate_threads` when you can stop early.

Scopes: `threads:read`.

**Parameters**

- `email` (`str`, required): The address. It does not have to be a saved contact.
- `limit` (`int`): Page size per request, from 1 to 100, defaulting to 25 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `q` (`str`): Searches inside those threads, with the mailbox search syntax, up to 200 characters.
- `sort` (`ContactThreadSort`): `newest` (the default), `oldest`, `sender` or `subject`.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[ContactThreadResource]` holding every thread across all pages.

**Example**

```python
from nextmail import nextmail

threads = nextmail.contacts.list_all_threads('ada@example.com')

print(len(threads), 'conversations with Ada')
```

**Notes**

- A failure on any page raises out of the whole call, and the threads already fetched are discarded.

Also available in: API [`GET /contacts/{email}/threads`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-email-threads); TypeScript [`nextmail.contacts.listAllThreads()`](https://nextmail.sh/docs/sdk/reference/contacts#listAllThreads); Ruby [`contacts.list_all_threads`](https://nextmail.sh/docs/ruby/reference/contacts#listAllThreads); PHP [`contacts->listAllThreads`](https://nextmail.sh/docs/php/reference/contacts#listAllThreads); Go [`Contacts.ListAllThreads`](https://nextmail.sh/docs/go/reference/contacts#listAllThreads); Java [`contacts().listAllThreads`](https://nextmail.sh/docs/java/reference/contacts#listAllThreads); C# [`Contacts.ListAllThreadsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listAllThreads).

### `contacts.iterate_threads()`

Stream the conversations with one person one thread at a time

```python
def iterate_threads(
    email: str,
    *,
    limit: int | None = None,
    cursor: str | None = None,
    q: str | None = None,
    sort: ContactThreadSort | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[ContactThreadResource]
```

Returns a generator that yields the threads `list_threads` lists for the address, one at a time, and requests the next page only once the current one is used up. Nothing is fetched until the loop starts, and leaving the loop with `break` stops the requests.

Scopes: `threads:read`.

**Parameters**

- `email` (`str`, required): The address. It does not have to be a saved contact.
- `limit` (`int`): Page size per request, from 1 to 100, defaulting to 25 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `q` (`str`): Searches inside those threads, with the mailbox search syntax, up to 200 characters.
- `sort` (`ContactThreadSort`): `newest` (the default), `oldest`, `sender` or `subject`.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[ContactThreadResource]`, a generator yielding one thread per step.

**Example**

```python
from nextmail import nextmail

for thread in nextmail.contacts.iterate_threads('ada@example.com'):
    if thread['hasUnread']:
        print('Unread:', thread['subject'])
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.

Also available in: API [`GET /contacts/{email}/threads`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-email-threads); TypeScript [`nextmail.contacts.iterateThreads()`](https://nextmail.sh/docs/sdk/reference/contacts#iterateThreads); Ruby [`contacts.iterate_threads`](https://nextmail.sh/docs/ruby/reference/contacts#iterateThreads); PHP [`contacts->iterateThreads`](https://nextmail.sh/docs/php/reference/contacts#iterateThreads); Go [`Contacts.IterateThreads`](https://nextmail.sh/docs/go/reference/contacts#iterateThreads); Java [`contacts().iterateThreads`](https://nextmail.sh/docs/java/reference/contacts#iterateThreads); C# [`Contacts.IterateThreadsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#iterateThreads).

### `contacts.activity()`

Read how mail with one person has gone over a window

```python
def activity(
    email: str,
    *,
    minutes: int | None = None,
    grain: TrackingGrain | None = None,
    offset_minutes: int | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactActivityResource
```

Returns the numbers behind the Activity tab on a contact in the app, over a window that ends now: messages received from the address and sent to it per bucket, the threads that moved, the threads whose newest message is theirs and so waits on a reply from the mailbox, when each side last wrote, and the median time each side takes to answer, in milliseconds.

`minutes=` sets how far back the window reaches, 90 days by default. `grain=` sets the bucket width and the key shape, `YYYY-MM-DD`, `YYYY-MM-DDTHH` or `YYYY-MM-DDTHH:MM`, and `offset_minutes=` shifts the boundaries so days break where the reader's day does. `buckets` is sparse and oldest first. Mail in the bin or in spam is left out.

Scopes: `threads:read`.

**Parameters**

- `email` (`str`, required): The address. It does not have to be a saved contact.
- `minutes` (`int`): Window length in minutes, from 1 to about 20 years. The server defaults to 90 days.
- `grain` (`TrackingGrain`): Bucket width: `minute`, `hour` or `day`, defaulting to `day`.
- `offset_minutes` (`int`): Minutes east of UTC to cut the buckets in, from -840 to 840, defaulting to 0. For the machine's own zone, pass `datetime.now().astimezone().utcoffset()` converted to whole minutes.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactActivityResource` with `object` set to `'contact_activity'`, `email`, `since`, `until`, `grain`, `buckets`, `totals` and `replyTime`. Each bucket has `bucket`, `received` and `sent`. `totals` has `received`, `sent`, `threads`, `waiting`, `lastReceivedAt` and `lastSentAt`, and `replyTime` has `yours` and `theirs`, each `None` when there is no reply to measure.

**Example**

```python
from nextmail import nextmail

activity = nextmail.contacts.activity('ada@example.com', minutes=30 * 24 * 60, grain='day')
totals = activity['totals']

print(totals['received'], 'in,', totals['sent'], 'out,', totals['waiting'], 'waiting on you')
```

**Notes**

- It needs `threads:read`, because it reads mail rather than the contact.
- Read only, so the SDK retries it after a network failure like any other read.
- A key limited to particular addresses or domains gets 422 `capability_unsupported` on `addressAllowlist`, because a contact's threads and activity are read from the mail of every address in the workspace.

Also available in: API [`GET /contacts/{email}/activity`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-email-activity); TypeScript [`nextmail.contacts.activity()`](https://nextmail.sh/docs/sdk/reference/contacts#activity); Ruby [`contacts.activity`](https://nextmail.sh/docs/ruby/reference/contacts#activity); PHP [`contacts->activity`](https://nextmail.sh/docs/php/reference/contacts#activity); Go [`Contacts.Activity`](https://nextmail.sh/docs/go/reference/contacts#activity); Java [`contacts().activity`](https://nextmail.sh/docs/java/reference/contacts#activity); C# [`Contacts.ActivityAsync`](https://nextmail.sh/docs/csharp/reference/contacts#activity); CLI [`nextmail contacts activity`](https://nextmail.sh/docs/cli/reference/contacts#contacts-activity).

### `contacts.list_events()`

List the events recorded for one contact

```python
def list_events(
    email: str,
    *,
    limit: int | None = None,
    cursor: str | None = None,
    name: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[ContactEventResource]
```

Returns one page of the events `events.send` recorded for a contact in the last 90 days, the most recent first by when they happened. Each has its `name`, `properties`, `occurredAt` and `mode`, which is `test` for an event a test key sent.

`limit=` takes 1 to 200 and defaults to 50. Pass `nextCursor` back as `cursor=`, with the same `name=`, while `hasMore` is `True`. `list_all_events` and `iterate_events` do that walk for you.

Scopes: `contacts:read`.

**Parameters**

- `email` (`str`, required): The contact, by email address, compared without case. The `contactId` an event or an enrollment carries works here too.
- `limit` (`int`): Events per page, a whole number from 1 to 200. The server defaults to 50.
- `cursor` (`str`): The `nextCursor` from the previous page, passed back exactly as it came. One that names no event of this contact is a 400 `invalid_cursor`.
- `name` (`str`): Only events with exactly this name.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[ContactEventResource]`, a dict with `items`, `hasMore` and `nextCursor`. Each item has `id`, `contactId`, `email`, `name`, `properties`, `occurredAt`, `mode` and `createdAt`.

**Example**

```python
from nextmail import nextmail

page = nextmail.contacts.list_events('ada@example.com', name='order.placed')

for event in page['items']:
    print(event['occurredAt'], event['name'], event['properties'])
```

**Notes**

- An address or id nobody you can reach has is 404 `contact_not_found`. An app a member connected reads only the contacts that member added.
- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /contacts/{email}/events`](https://nextmail.sh/docs/api/reference/events#get-contacts-email-events); TypeScript [`nextmail.contacts.listEvents()`](https://nextmail.sh/docs/sdk/reference/contacts#listEvents); Ruby [`contacts.list_events`](https://nextmail.sh/docs/ruby/reference/contacts#listEvents); PHP [`contacts->listEvents`](https://nextmail.sh/docs/php/reference/contacts#listEvents); Go [`Contacts.ListEvents`](https://nextmail.sh/docs/go/reference/contacts#listEvents); Java [`contacts().listEvents`](https://nextmail.sh/docs/java/reference/contacts#listEvents); C# [`Contacts.ListEventsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listEvents); CLI [`nextmail contacts list-events`](https://nextmail.sh/docs/cli/reference/contacts#contacts-list-events).

### `contacts.list_all_events()`

Collect every event of one contact into one list

```python
def list_all_events(
    email: str,
    *,
    limit: int | None = None,
    cursor: str | None = None,
    name: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[ContactEventResource]
```

Follows `nextCursor` from page to page and returns every event recorded for the contact in the last 90 days as one list, the most recent first, in the shape `list_events` returns. `limit=` sets the page size of each request, not the total.

Scopes: `contacts:read`.

**Parameters**

- `email` (`str`, required): The contact, by email address, compared without case. The `contactId` an event or an enrollment carries works here too.
- `limit` (`int`): Page size per request, from 1 to 200, defaulting to 50 on the server.
- `cursor` (`str`): A `nextCursor` from an earlier page to start after.
- `name` (`str`): Only events with exactly this name.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[ContactEventResource]` holding every matching event across all pages.

**Example**

```python
from nextmail import nextmail

events = nextmail.contacts.list_all_events('ada@example.com')

names = {event['name'] for event in events}

print(len(events), sorted(names))
```

**Notes**

- A failure on any page raises, and the events already fetched are discarded.

Also available in: API [`GET /contacts/{email}/events`](https://nextmail.sh/docs/api/reference/events#get-contacts-email-events); TypeScript [`nextmail.contacts.listAllEvents()`](https://nextmail.sh/docs/sdk/reference/contacts#listAllEvents); Ruby [`contacts.list_all_events`](https://nextmail.sh/docs/ruby/reference/contacts#listAllEvents); PHP [`contacts->listAllEvents`](https://nextmail.sh/docs/php/reference/contacts#listAllEvents); Go [`Contacts.ListAllEvents`](https://nextmail.sh/docs/go/reference/contacts#listAllEvents); Java [`contacts().listAllEvents`](https://nextmail.sh/docs/java/reference/contacts#listAllEvents); C# [`Contacts.ListAllEventsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listAllEvents).

### `contacts.iterate_events()`

Stream the events of one contact one at a time

```python
def iterate_events(
    email: str,
    *,
    limit: int | None = None,
    cursor: str | None = None,
    name: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[ContactEventResource]
```

Returns a generator that yields the events of a contact one by one, the most recent first, and requests the next page only once the current one is used up. Breaking out of the loop stops the requests.

Scopes: `contacts:read`.

**Parameters**

- `email` (`str`, required): The contact, by email address, compared without case. The `contactId` an event or an enrollment carries works here too.
- `limit` (`int`): Page size per request, from 1 to 200, defaulting to 50 on the server.
- `cursor` (`str`): A `nextCursor` from an earlier page to start after.
- `name` (`str`): Only events with exactly this name.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[ContactEventResource]`, a generator yielding one event per step.

**Example**

```python
from nextmail import nextmail

for event in nextmail.contacts.iterate_events('ada@example.com'):
    if event['name'] == 'plan.cancelled':
        print('cancelled on', event['occurredAt'])

        break
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages it read.

Also available in: API [`GET /contacts/{email}/events`](https://nextmail.sh/docs/api/reference/events#get-contacts-email-events); TypeScript [`nextmail.contacts.iterateEvents()`](https://nextmail.sh/docs/sdk/reference/contacts#iterateEvents); Ruby [`contacts.iterate_events`](https://nextmail.sh/docs/ruby/reference/contacts#iterateEvents); PHP [`contacts->iterateEvents`](https://nextmail.sh/docs/php/reference/contacts#iterateEvents); Go [`Contacts.IterateEvents`](https://nextmail.sh/docs/go/reference/contacts#iterateEvents); Java [`contacts().iterateEvents`](https://nextmail.sh/docs/java/reference/contacts#iterateEvents); C# [`Contacts.IterateEventsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#iterateEvents).

### `contacts.list_cards()`

List the address book as contact cards

```python
def list_cards(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    email: str | None = None,
    without_email: bool | None = None,
    kind: ContactCardKind | None = None,
    group_id: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[ContactCardResource]
```

Returns one page of contact cards, newest first. A card is a saved contact with everything an address book keeps for it: phone numbers, other email addresses, postal addresses, websites, organisation, department, job title, birthday, nickname and the name in parts. Cards with no email address, such as a plumber saved on a phone, are listed too. It is the address book phones and computers sync over CardDAV.

`email=` finds the card of one saved contact, and `without_email=True` lists only the cards with no address. An address that was only ever mailed, and never saved, has no card.

Paging is keyset. `limit=` takes 1 to 200 and defaults to 50, and `nextCursor` goes back as `cursor=` while `hasMore` is `True`. Never build a cursor yourself.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Cards per page, a whole number from 1 to 200. The server defaults to 50.
- `cursor` (`str`): The `nextCursor` from the previous page. Never build one yourself.
- `email` (`str`): Only the card of the saved contact with this address.
- `without_email` (`bool`): `True` lists only the cards that have no email address.
- `kind` (`ContactCardKind`): `'group'` lists only the groups, `'individual'` only the people and companies, one of `CONTACT_CARD_KINDS`.
- `group_id` (`str`): Only the cards in the group with this id.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[ContactCardResource]`, a dict with `items`, `hasMore` and `nextCursor`. Each card has `id`, `uid`, `email`, `name`, `nameParts`, `nickname`, `organization`, `department`, `title`, `birthday`, `notes`, the lists `emails`, `phones`, `addresses` and `urls`, `photoUrl`, `createdAt` and `updatedAt`.

**Example**

```python
from nextmail import nextmail

page = nextmail.contacts.list_cards(without_email=True)

for card in page['items']:
    print(card['name'], [phone['value'] for phone in card['phones']])

print(page['hasMore'], page['nextCursor'])
```

**Notes**

- Every member reads the same address book through a key, since a key acts for the workspace owner.
- A cursor that names no card is a 400 `invalid_cursor`. Start again without one.

Also available in: API [`GET /contacts/cards`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-cards); TypeScript [`nextmail.contacts.listCards()`](https://nextmail.sh/docs/sdk/reference/contacts#listCards); Ruby [`contacts.list_cards`](https://nextmail.sh/docs/ruby/reference/contacts#listCards); PHP [`contacts->listCards`](https://nextmail.sh/docs/php/reference/contacts#listCards); Go [`Contacts.ListCards`](https://nextmail.sh/docs/go/reference/contacts#listCards); Java [`contacts().listCards`](https://nextmail.sh/docs/java/reference/contacts#listCards); C# [`Contacts.ListCardsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listCards); CLI [`nextmail contacts list-cards`](https://nextmail.sh/docs/cli/reference/contacts#contacts-list-cards).

### `contacts.list_all_cards()`

Collect every contact card into one list

```python
def list_all_cards(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    email: str | None = None,
    without_email: bool | None = None,
    kind: ContactCardKind | None = None,
    group_id: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[ContactCardResource]
```

Follows `nextCursor` from page to page and returns every card in the address book in one list, newest first. It takes the same `email=` and `without_email=` filters as `list_cards`.

Everything is held in memory before the call returns. Prefer `iterate_cards` when you can stop early. `limit=` sets the page size of each request, not the total.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Page size per request, from 1 to 200, defaulting to 50 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `email` (`str`): Only the card of the saved contact with this address.
- `without_email` (`bool`): `True` lists only the cards that have no email address.
- `kind` (`ContactCardKind`): `'group'` lists only the groups, `'individual'` only the people and companies, one of `CONTACT_CARD_KINDS`.
- `group_id` (`str`): Only the cards in the group with this id.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[ContactCardResource]` holding every card across all pages.

**Example**

```python
from nextmail import nextmail

cards = nextmail.contacts.list_all_cards()

print(len(cards), 'cards in the address book')
```

**Notes**

- A failure on any page raises out of the whole call, and the cards already fetched are discarded.

Also available in: API [`GET /contacts/cards`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-cards); TypeScript [`nextmail.contacts.listAllCards()`](https://nextmail.sh/docs/sdk/reference/contacts#listAllCards); Ruby [`contacts.list_all_cards`](https://nextmail.sh/docs/ruby/reference/contacts#listAllCards); PHP [`contacts->listAllCards`](https://nextmail.sh/docs/php/reference/contacts#listAllCards); Go [`Contacts.ListAllCards`](https://nextmail.sh/docs/go/reference/contacts#listAllCards); Java [`contacts().listAllCards`](https://nextmail.sh/docs/java/reference/contacts#listAllCards); C# [`Contacts.ListAllCardsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listAllCards).

### `contacts.iterate_cards()`

Stream the address book one card at a time

```python
def iterate_cards(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    email: str | None = None,
    without_email: bool | None = None,
    kind: ContactCardKind | None = None,
    group_id: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[ContactCardResource]
```

Returns a generator that yields cards one at a time, newest first, and requests the next page only once the current one is used up. Nothing is fetched until the loop starts, and leaving the loop with `break` stops the requests. It takes the same `email=` and `without_email=` filters as `list_cards`.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Page size per request, from 1 to 200, defaulting to 50 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `email` (`str`): Only the card of the saved contact with this address.
- `without_email` (`bool`): `True` lists only the cards that have no email address.
- `kind` (`ContactCardKind`): `'group'` lists only the groups, `'individual'` only the people and companies, one of `CONTACT_CARD_KINDS`.
- `group_id` (`str`): Only the cards in the group with this id.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[ContactCardResource]`, a generator yielding one card per step.

**Example**

```python
from nextmail import nextmail

for card in nextmail.contacts.iterate_cards():
    if card['birthday']:
        print(card['name'], card['birthday'])
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.

Also available in: API [`GET /contacts/cards`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-cards); TypeScript [`nextmail.contacts.iterateCards()`](https://nextmail.sh/docs/sdk/reference/contacts#iterateCards); Ruby [`contacts.iterate_cards`](https://nextmail.sh/docs/ruby/reference/contacts#iterateCards); PHP [`contacts->iterateCards`](https://nextmail.sh/docs/php/reference/contacts#iterateCards); Go [`Contacts.IterateCards`](https://nextmail.sh/docs/go/reference/contacts#iterateCards); Java [`contacts().iterateCards`](https://nextmail.sh/docs/java/reference/contacts#iterateCards); C# [`Contacts.IterateCardsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#iterateCards).

### `contacts.get_card()`

Read one contact card

```python
def get_card(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactCardResource
```

Returns one card with everything it holds. To find the card of a saved contact by its address, pass `email=` to `list_cards`.

Scopes: `contacts:read`.

**Parameters**

- `id` (`str`, required): The card id, `ccd_` followed by 24 characters, from `list_cards` or `get_card`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactCardResource` with `id`, `uid`, the vCard UID that phones and computers know the card by, `email`, which is `None` on a card with no address, `name`, `nameParts`, `nickname`, `organization`, `department`, `title`, `birthday`, `notes`, `photoUrl`, `createdAt` and `updatedAt`. `emails`, `phones` and `urls` are lists of dicts with `value`, `label` and `customLabel`, and `addresses` is a list of dicts with `label`, `customLabel`, `street`, `locality`, `region`, `postalCode` and `country`.

**Example**

```python
from nextmail import nextmail

card = nextmail.contacts.get_card('ccd_0123456789abcdef01234567')

print(card['name'], card['organization'], [phone['value'] for phone in card['phones']])
```

**Notes**

- An id that names no card is a 404 `contact_card_not_found`, which raises `NextmailApiError` with `is_not_found`.

Also available in: API [`GET /contacts/cards/{id}`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-cards-id); TypeScript [`nextmail.contacts.getCard()`](https://nextmail.sh/docs/sdk/reference/contacts#getCard); Ruby [`contacts.get_card`](https://nextmail.sh/docs/ruby/reference/contacts#getCard); PHP [`contacts->getCard`](https://nextmail.sh/docs/php/reference/contacts#getCard); Go [`Contacts.GetCard`](https://nextmail.sh/docs/go/reference/contacts#getCard); Java [`contacts().getCard`](https://nextmail.sh/docs/java/reference/contacts#getCard); C# [`Contacts.GetCardAsync`](https://nextmail.sh/docs/csharp/reference/contacts#getCard); CLI [`nextmail contacts get-card`](https://nextmail.sh/docs/cli/reference/contacts#contacts-get-card).

### `contacts.create_card()`

Add a card to the address book

```python
def create_card(
    body: ContactCardCreate,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactCardResource
```

Adds a contact card. With `email` the card is a saved contact as well, the same one `create` makes, and an address the workspace already knows from mail becomes saved. Without one it is a card on its own, kept for its name, phone numbers and the rest. A card needs at least a name, an email address, a phone number or an organisation.

Each entry in `emails`, `phones` and `urls` is a dict with `value`, and optionally `label`, one of `CONTACT_CARD_LABELS`, and `customLabel`, a label of your own.

Phones and computers that sync the address book over CardDAV get the new card on their next sync.

Scopes: `contacts:write`.

**Parameters**

- `body['email']` (`str | None`): The address Nextmail knows the contact by. With it the card is a saved contact as well. A saved contact keeps its address, so put any other address in `emails`.
- `body['name']` (`str | None`): The full name the address book shows, up to 200 characters.
- `body['nameParts']` (`ContactCardNameInput | None`): The name in parts, a dict with `prefix`, `given`, `middle`, `family` and `suffix`, the way phones keep it.
- `body['nickname']` (`str | None`): A nickname.
- `body['organization']` (`str | None`): The company or organisation.
- `body['department']` (`str | None`): The department within the organisation.
- `body['title']` (`str | None`): The job title.
- `body['birthday']` (`str | None`): `YYYY-MM-DD`, or `--MM-DD` when the year is not known.
- `body['notes']` (`str | None`): Free-form notes. On a saved contact these are the notes the contact page shows.
- `body['emails']` (`list[ContactCardValueInput]`): Other email addresses besides `email`, each a dict with `value`, and optionally `label` and `customLabel`.
- `body['phones']` (`list[ContactCardValueInput]`): Phone numbers, each a dict with `value`, and optionally `label` and `customLabel`.
- `body['addresses']` (`list[ContactCardAddressInput]`): Postal addresses, each a dict with any of `label`, `customLabel`, `street`, `locality`, `region`, `postalCode` and `country`.
- `body['urls']` (`list[ContactCardValueInput]`): Websites and profile links, each a dict with `value`, and optionally `label` and `customLabel`.
- `body['kind']` (`ContactCardKind`): `'group'` makes a group, the kind phones keep, which needs `name` and takes `members` instead of an address. `'individual'` when left out.
- `body['members']` (`list[str]`): For a group, the ids of the cards in it, up to 2,000. Groups cannot hold other groups.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactCardResource` for the new card.

**Example**

```python
from nextmail import nextmail

card = nextmail.contacts.create_card(
    {
        'email': 'ada@example.com',
        'name': 'Ada Lovelace',
        'organization': 'Analytical Engines',
        'phones': [{'value': '+44 20 7946 0000', 'label': 'work'}],
        'birthday': '1815-12-10',
    }
)

print(card['id'], card['email'])
```

**Notes**

- The SDK does not retry it after a network failure, because a second call would make a second card.
- An address that already belongs to another card is a 409 `contact_card_email_taken`, and a card with nothing to keep a 422 `invalid_contact_card`.
- A workspace that already keeps as many cards as it may gets a 409 `contact_card_limit`. Delete some before you add more.

Also available in: API [`POST /contacts/cards`](https://nextmail.sh/docs/api/reference/contacts#post-contacts-cards); TypeScript [`nextmail.contacts.createCard()`](https://nextmail.sh/docs/sdk/reference/contacts#createCard); Ruby [`contacts.create_card`](https://nextmail.sh/docs/ruby/reference/contacts#createCard); PHP [`contacts->createCard`](https://nextmail.sh/docs/php/reference/contacts#createCard); Go [`Contacts.CreateCard`](https://nextmail.sh/docs/go/reference/contacts#createCard); Java [`contacts().createCard`](https://nextmail.sh/docs/java/reference/contacts#createCard); C# [`Contacts.CreateCardAsync`](https://nextmail.sh/docs/csharp/reference/contacts#createCard); CLI [`nextmail contacts create-card`](https://nextmail.sh/docs/cli/reference/contacts#contacts-create-card).

### `contacts.update_card()`

Change a contact card

```python
def update_card(
    id: str,
    patch: ContactCardPatch,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactCardResource
```

Changes the fields you send and leaves the rest alone. `None` clears a field, and a list you send (`emails`, `phones`, `addresses` or `urls`) replaces the whole list, so read the card first when you mean to add one entry.

On a saved contact, `name` and `notes` are the contact's own, so the contact page shows the change. `email` can only be given to a card that has none, which makes it a saved contact. A saved contact keeps its address: add another address to `emails` instead.

Phones and computers that sync the address book over CardDAV get the change on their next sync.

Scopes: `contacts:write`.

**Parameters**

- `id` (`str`, required): The card id, `ccd_` followed by 24 characters, from `list_cards` or `get_card`.
- `patch['email']` (`str | None`): The address Nextmail knows the contact by. Given to a card that has none, it makes the card a saved contact. A saved contact keeps its address, so put any other address in `emails`.
- `patch['name']` (`str | None`): The full name the address book shows, up to 200 characters.
- `patch['nameParts']` (`ContactCardNameInput | None`): The name in parts, a dict with `prefix`, `given`, `middle`, `family` and `suffix`, the way phones keep it.
- `patch['nickname']` (`str | None`): A nickname.
- `patch['organization']` (`str | None`): The company or organisation.
- `patch['department']` (`str | None`): The department within the organisation.
- `patch['title']` (`str | None`): The job title.
- `patch['birthday']` (`str | None`): `YYYY-MM-DD`, or `--MM-DD` when the year is not known.
- `patch['notes']` (`str | None`): Free-form notes. On a saved contact these are the notes the contact page shows.
- `patch['emails']` (`list[ContactCardValueInput]`): Other email addresses besides `email`, each a dict with `value`, and optionally `label` and `customLabel`. The list replaces the one the card has.
- `patch['phones']` (`list[ContactCardValueInput]`): Phone numbers, each a dict with `value`, and optionally `label` and `customLabel`. The list replaces the one the card has.
- `patch['addresses']` (`list[ContactCardAddressInput]`): Postal addresses, each a dict with any of `label`, `customLabel`, `street`, `locality`, `region`, `postalCode` and `country`. The list replaces the one the card has.
- `patch['urls']` (`list[ContactCardValueInput]`): Websites and profile links, each a dict with `value`, and optionally `label` and `customLabel`. The list replaces the one the card has.
- `patch['kind']` (`ContactCardKind`): What the card already is. A person cannot become a group or a group a person, so any other value is a 422 `invalid_contact_card`.
- `patch['members']` (`list[str]`): For a group, the ids of the cards in it. Given, it replaces the whole list, so send the ids already in it with the ones you add.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactCardResource` as it stands after the change.

**Example**

```python
from nextmail import nextmail

card = nextmail.contacts.update_card(
    'ccd_0123456789abcdef01234567',
    {
        'title': 'Head of Research',
        'nickname': None,
        'phones': [
            {'value': '+44 20 7946 0000', 'label': 'work'},
            {'value': '+1 555 0100', 'label': 'mobile'},
        ],
    },
)

print(card['title'], [phone['value'] for phone in card['phones']])
```

**Notes**

- The SDK retries this call after a network failure, since the same patch sent twice leaves the same card.
- Changing the address of a saved contact is a 422 `invalid_contact_card`, and an address that already belongs to another card a 409 `contact_card_email_taken`.
- An id that names no card is a 404 `contact_card_not_found`.

Also available in: API [`PATCH /contacts/cards/{id}`](https://nextmail.sh/docs/api/reference/contacts#patch-contacts-cards-id); TypeScript [`nextmail.contacts.updateCard()`](https://nextmail.sh/docs/sdk/reference/contacts#updateCard); Ruby [`contacts.update_card`](https://nextmail.sh/docs/ruby/reference/contacts#updateCard); PHP [`contacts->updateCard`](https://nextmail.sh/docs/php/reference/contacts#updateCard); Go [`Contacts.UpdateCard`](https://nextmail.sh/docs/go/reference/contacts#updateCard); Java [`contacts().updateCard`](https://nextmail.sh/docs/java/reference/contacts#updateCard); C# [`Contacts.UpdateCardAsync`](https://nextmail.sh/docs/csharp/reference/contacts#updateCard); CLI [`nextmail contacts update-card`](https://nextmail.sh/docs/cli/reference/contacts#contacts-update-card).

### `contacts.delete_card()`

Delete a contact card

```python
def delete_card(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> DeletedContactCardResource
```

Removes the card from the address book and from every phone and computer that syncs it. On a saved contact it is the same as `delete`: the contact goes with its notes, photo and audiences, and the address is hidden from the people list and the suggestions.

Scopes: `contacts:write`.

**Parameters**

- `id` (`str`, required): The card id, `ccd_` followed by 24 characters, from `list_cards` or `get_card`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`DeletedContactCardResource` with `object` set to `'contact_card'`, `id` and `deleted` set to `True`.

**Example**

```python
from nextmail import nextmail

removed = nextmail.contacts.delete_card('ccd_0123456789abcdef01234567')

print(removed['id'], removed['deleted'])
```

**Notes**

- There is no undo, and the SDK does not retry it after a network failure.
- An id that names no card is a 404 `contact_card_not_found`.

Also available in: API [`DELETE /contacts/cards/{id}`](https://nextmail.sh/docs/api/reference/contacts#delete-contacts-cards-id); TypeScript [`nextmail.contacts.deleteCard()`](https://nextmail.sh/docs/sdk/reference/contacts#deleteCard); Ruby [`contacts.delete_card`](https://nextmail.sh/docs/ruby/reference/contacts#deleteCard); PHP [`contacts->deleteCard`](https://nextmail.sh/docs/php/reference/contacts#deleteCard); Go [`Contacts.DeleteCard`](https://nextmail.sh/docs/go/reference/contacts#deleteCard); Java [`contacts().deleteCard`](https://nextmail.sh/docs/java/reference/contacts#deleteCard); C# [`Contacts.DeleteCardAsync`](https://nextmail.sh/docs/csharp/reference/contacts#deleteCard); CLI [`nextmail contacts delete-card`](https://nextmail.sh/docs/cli/reference/contacts#contacts-delete-card).

### `contacts.find_duplicate_cards()`

Find duplicate contact cards

```python
def find_duplicate_cards(
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactCardDuplicateListResource
```

Finds the cards in the address book that look like the same person, as Find duplicates on the Contacts page of the app does: cards that share an email address or a phone number, or that have the same full name. Each group in `data` lists its cards with the most complete one first, which is the card to keep when you pass the rest to `merge_cards`.

Groups found by a shared address come first, then by phone number, then by name. Phone numbers match on their last nine digits, so `+44 20 7946 0000` and `020 7946 0000` are the same number. Group cards are never compared.

Scopes: `contacts:read`.

**Parameters**

- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactCardDuplicateListResource`, a dict with `data`, each group a dict with `object` set to `'contact_card_duplicates'`, `reason`, one of `CONTACT_CARD_DUPLICATE_REASONS`, and `cards`, and `hasMore`, `True` when more than 100 groups were found.

**Example**

```python
from nextmail import nextmail

found = nextmail.contacts.find_duplicate_cards()

for group in found['data']:
    keep, *others = group['cards']
    print(group['reason'], keep['name'], [other['id'] for other in others])
```

**Notes**

- It reads every card, so call it when somebody asks rather than on every page load.
- With `hasMore`, merge the groups you have and call it again for the rest.

Also available in: API [`GET /contacts/cards/duplicates`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-cards-duplicates); TypeScript [`nextmail.contacts.findDuplicateCards()`](https://nextmail.sh/docs/sdk/reference/contacts#findDuplicateCards); Ruby [`contacts.find_duplicate_cards`](https://nextmail.sh/docs/ruby/reference/contacts#findDuplicateCards); PHP [`contacts->findDuplicateCards`](https://nextmail.sh/docs/php/reference/contacts#findDuplicateCards); Go [`Contacts.FindDuplicateCards`](https://nextmail.sh/docs/go/reference/contacts#findDuplicateCards); Java [`contacts().findDuplicateCards`](https://nextmail.sh/docs/java/reference/contacts#findDuplicateCards); C# [`Contacts.FindDuplicateCardsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#findDuplicateCards); CLI [`nextmail contacts find-duplicate-cards`](https://nextmail.sh/docs/cli/reference/contacts#contacts-find-duplicate-cards).

### `contacts.merge_cards()`

Merge contact cards

```python
def merge_cards(
    id: str,
    body: ContactCardMerge,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactCardResource
```

Folds other cards into the card `id` names and deletes them, as Merge does in Find duplicates on the Contacts page of the app. Fields the kept card leaves empty are filled from the others, notes are joined, and every email address, phone number, postal address and website is kept. Groups that held the other cards hold the kept one instead.

A saved contact among the others stays known by its address, in the people list and in its audiences, but it leaves the address book. When the kept card has no address of its own, it becomes the saved contact of the first address the others had. Phones and computers that sync the address book over CardDAV get the change on their next sync.

Scopes: `contacts:write`.

**Parameters**

- `id` (`str`, required): The card to keep, from `list_cards`, `get_card` or `find_duplicate_cards`.
- `body['cardIds']` (`list[str]`, required): The cards to fold into it, 1 to 10 of them. They are deleted once their details are kept.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactCardResource` for the card that kept everything.

**Example**

```python
from nextmail import nextmail

card = nextmail.contacts.merge_cards(
    'ccd_0123456789abcdef01234567', {'cardIds': ['ccd_89abcdef0123456789abcdef']}
)

print(card['name'], len(card['emails']), 'other addresses')
```

**Notes**

- There is no undo, and the SDK does not retry it after a network failure.
- Naming the kept card among `cardIds`, or a group anywhere, is a 422 `invalid_contact_card`, and an id that names no card a 404 `contact_card_not_found`.

Also available in: API [`POST /contacts/cards/{id}/merge`](https://nextmail.sh/docs/api/reference/contacts#post-contacts-cards-id-merge); TypeScript [`nextmail.contacts.mergeCards()`](https://nextmail.sh/docs/sdk/reference/contacts#mergeCards); Ruby [`contacts.merge_cards`](https://nextmail.sh/docs/ruby/reference/contacts#mergeCards); PHP [`contacts->mergeCards`](https://nextmail.sh/docs/php/reference/contacts#mergeCards); Go [`Contacts.MergeCards`](https://nextmail.sh/docs/go/reference/contacts#mergeCards); Java [`contacts().mergeCards`](https://nextmail.sh/docs/java/reference/contacts#mergeCards); C# [`Contacts.MergeCardsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#mergeCards); CLI [`nextmail contacts merge-cards`](https://nextmail.sh/docs/cli/reference/contacts#contacts-merge-cards).

### `contacts.list_birthdays()`

List birthdays

```python
def list_birthdays(
    *,
    from_: datetime | str,
    to: datetime | str,
    timezone: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[ContactBirthdayResource]
```

Returns the birthdays of the people in the address book that fall between `from_` and `to`, the birthdays calendar the Calendar page of the app can show. There is one entry for each date, so a window of a whole year can hold somebody twice, and `age` is how old they turn that day when the card holds the year of birth. Somebody born on 29 February has their birthday on 28 February in other years.

`from_` and `to` take a `datetime` or an ISO 8601 string and may be at most 366 days apart. `timezone`, `UTC` unless you say, decides which day the window starts and ends on.

Scopes: `contacts:read`.

**Parameters**

- `from_` (`datetime | str`, required): The start of the window, a `datetime` or an ISO 8601 string.
- `to` (`datetime | str`, required): The end of the window, after `from_` and at most 366 days later.
- `timezone` (`str`): IANA zone such as `Europe/London`, at most 64 characters. Defaults to `UTC`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[ContactBirthdayResource]` sorted by date and then by name, each with `cardId`, `contactId`, `email`, `name`, `birthday`, `date`, `age` and `photoUrl`.

**Example**

```python
from datetime import datetime, timedelta, timezone

from nextmail import nextmail

start = datetime.now(timezone.utc)
birthdays = nextmail.contacts.list_birthdays(
    from_=start, to=start + timedelta(days=30), timezone='Europe/London'
)

for birthday in birthdays:
    print(birthday['date'], birthday['name'], birthday['age'] or 'age not known')
```

**Notes**

- A value that does not parse, a reversed or overlong window, or an unknown zone is a 400 `invalid_parameter` naming the field.

Also available in: API [`GET /contacts/birthdays`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-birthdays); TypeScript [`nextmail.contacts.listBirthdays()`](https://nextmail.sh/docs/sdk/reference/contacts#listBirthdays); Ruby [`contacts.list_birthdays`](https://nextmail.sh/docs/ruby/reference/contacts#listBirthdays); PHP [`contacts->listBirthdays`](https://nextmail.sh/docs/php/reference/contacts#listBirthdays); Go [`Contacts.ListBirthdays`](https://nextmail.sh/docs/go/reference/contacts#listBirthdays); Java [`contacts().listBirthdays`](https://nextmail.sh/docs/java/reference/contacts#listBirthdays); C# [`Contacts.ListBirthdaysAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listBirthdays); CLI [`nextmail contacts list-birthdays`](https://nextmail.sh/docs/cli/reference/contacts#contacts-list-birthdays).

### `contacts.import_vcf()`

Import a contacts file

```python
def import_vcf(
    data: RawBody,
    *,
    filename: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactImportResource
```

Sends a vCard (.vcf) file as the request body and queues it. Every card in it becomes a contact card, with its phone numbers, addresses and the rest. Poll `get_import` until `status` is `completed`.

The file may be 20 MB. Imported contacts join no audience, so a broadcast never reaches them because of an import. A card whose address or UID is already in the address book is counted under `merged` and left as it is, and a card with no address is matched on its phone number. A workspace keeps 20,000 cards, and the cards past that are counted under `skipped`.

`data` is `bytes`, a `bytearray` or a `memoryview`, and it goes out as `text/vcard`.

Scopes: `contacts:write`.

**Parameters**

- `data` (`RawBody`, required): The .vcf file as `bytes`, a `bytearray` or a `memoryview`.
- `filename` (`str`): The name to show for the file in the list of imports, at most 255 characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactImportResource` with `status` set to `queued`.

**Example**

```python
from pathlib import Path

from nextmail import nextmail

queued = nextmail.contacts.import_vcf(
    Path('contacts.vcf').read_bytes(), filename='contacts.vcf'
)

print(queued['id'], queued['status'])
```

**Notes**

- A body that holds no vCard is a 400 `not_a_contacts_file`, and an empty one a 400 `item_import_empty`.
- A file over 20 MB is a 413 `item_import_too_large`.
- To send text you already hold, encode it first with `text.encode()`.
- An upload that outlasts the client's `timeout` raises `NextmailNetworkError` with `is_timeout`, so pass a longer `timeout=` for a large file on a slow connection.
- Not retried automatically by the SDK.

Also available in: API [`POST /contacts/imports`](https://nextmail.sh/docs/api/reference/contacts#post-contacts-imports); TypeScript [`nextmail.contacts.importVcf()`](https://nextmail.sh/docs/sdk/reference/contacts#importVcf); Ruby [`contacts.import_vcf`](https://nextmail.sh/docs/ruby/reference/contacts#importVcf); PHP [`contacts->importVcf`](https://nextmail.sh/docs/php/reference/contacts#importVcf); Go [`Contacts.ImportVcf`](https://nextmail.sh/docs/go/reference/contacts#importVcf); Java [`contacts().importVcf`](https://nextmail.sh/docs/java/reference/contacts#importVcf); C# [`Contacts.ImportVcfAsync`](https://nextmail.sh/docs/csharp/reference/contacts#importVcf); CLI [`nextmail contacts import-vcf`](https://nextmail.sh/docs/cli/reference/contacts#contacts-import-vcf).

### `contacts.list_imports()`

List contacts imports

```python
def list_imports(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[ContactImportResource]
```

Returns one page of the contacts files and address books brought in, newest first, each with what it added and what it left out. Beside the files sent with `import_vcf` it lists the address books that came with a mailbox import, and those carry the id of that import in `parentImportId`.

Paging is keyset. `limit=` takes 1 to 100 and defaults to 25, and `nextCursor` goes back as `cursor=` while `hasMore` is `True`. `list_all_imports` and `iterate_imports` do that walk for you.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Imports per page, a whole number from 1 to 100. The server defaults to 25.
- `cursor` (`str`): The `nextCursor` from the previous page. Never build one yourself.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[ContactImportResource]`, a dict with `items`, `hasMore` and `nextCursor`.

**Example**

```python
from nextmail import nextmail

page = nextmail.contacts.list_imports(limit=50)

for item in page['items']:
    counts = item['counts']
    print(item['fileName'], item['status'], counts['imported'], counts['merged'])
```

**Notes**

- `source` is `file` for an upload. `takeout`, `carddav` and `microsoft` came with a mailbox import.
- A cursor that names no import is a 400 `invalid_cursor`.
- A GET is retried automatically on network failure and on 408, 500, 502, 503 and 504 responses, up to the client's `max_retries`, and on a 429 only when it carries a `Retry-After` of a minute or less.

Also available in: API [`GET /contacts/imports`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-imports); TypeScript [`nextmail.contacts.listImports()`](https://nextmail.sh/docs/sdk/reference/contacts#listImports); Ruby [`contacts.list_imports`](https://nextmail.sh/docs/ruby/reference/contacts#listImports); PHP [`contacts->listImports`](https://nextmail.sh/docs/php/reference/contacts#listImports); Go [`Contacts.ListImports`](https://nextmail.sh/docs/go/reference/contacts#listImports); Java [`contacts().listImports`](https://nextmail.sh/docs/java/reference/contacts#listImports); C# [`Contacts.ListImportsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listImports); CLI [`nextmail contacts list-imports`](https://nextmail.sh/docs/cli/reference/contacts#contacts-list-imports).

### `contacts.list_all_imports()`

Collect every contacts import into one list

```python
def list_all_imports(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[ContactImportResource]
```

Follows `nextCursor` from page to page and returns every contacts import in one list, newest first.

Everything is held in memory before the call returns. Prefer `iterate_imports` when you can stop early. `limit=` sets the page size of each request, not the total.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Page size per request, from 1 to 100, defaulting to 25 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[ContactImportResource]` holding every import across all pages.

**Example**

```python
from nextmail import nextmail

imports = nextmail.contacts.list_all_imports()
added = sum(item['counts']['imported'] for item in imports)

print(f'{added} cards came from {len(imports)} imports')
```

**Notes**

- A failure on any page raises out of the whole call, and the imports already fetched are discarded.

Also available in: API [`GET /contacts/imports`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-imports); TypeScript [`nextmail.contacts.listAllImports()`](https://nextmail.sh/docs/sdk/reference/contacts#listAllImports); Ruby [`contacts.list_all_imports`](https://nextmail.sh/docs/ruby/reference/contacts#listAllImports); PHP [`contacts->listAllImports`](https://nextmail.sh/docs/php/reference/contacts#listAllImports); Go [`Contacts.ListAllImports`](https://nextmail.sh/docs/go/reference/contacts#listAllImports); Java [`contacts().listAllImports`](https://nextmail.sh/docs/java/reference/contacts#listAllImports); C# [`Contacts.ListAllImportsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#listAllImports).

### `contacts.iterate_imports()`

Stream contacts imports one at a time

```python
def iterate_imports(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[ContactImportResource]
```

Returns a generator that yields contacts imports one at a time, newest first, and requests the next page only once the current one is used up. Nothing is fetched until the loop starts, and leaving the loop with `break` stops the requests.

Scopes: `contacts:read`.

**Parameters**

- `limit` (`int`): Page size per request, from 1 to 100, defaulting to 25 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[ContactImportResource]`, a generator yielding one import per step.

**Example**

```python
from nextmail import nextmail

for item in nextmail.contacts.iterate_imports():
    if item['status'] != 'running':
        continue

    print('still running', item['id'], item['counts']['total'])
    break
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.

Also available in: API [`GET /contacts/imports`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-imports); TypeScript [`nextmail.contacts.iterateImports()`](https://nextmail.sh/docs/sdk/reference/contacts#iterateImports); Ruby [`contacts.iterate_imports`](https://nextmail.sh/docs/ruby/reference/contacts#iterateImports); PHP [`contacts->iterateImports`](https://nextmail.sh/docs/php/reference/contacts#iterateImports); Go [`Contacts.IterateImports`](https://nextmail.sh/docs/go/reference/contacts#iterateImports); Java [`contacts().iterateImports`](https://nextmail.sh/docs/java/reference/contacts#iterateImports); C# [`Contacts.IterateImportsAsync`](https://nextmail.sh/docs/csharp/reference/contacts#iterateImports).

### `contacts.get_import()`

Get a contacts import

```python
def get_import(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactImportResource
```

Returns one contacts import with its counts and its report: the files it read, how many cards it left out and why, and up to 20 of the cards it left out. Poll it after `import_vcf` until `status` is `completed` or `failed`.

`counts['imported']` is what was added, `counts['merged']` the cards left as they were because they were already in the address book, and `counts['skipped']` what was left out, which `report['skipped']` breaks down by reason: `limit` for a card past the 20,000 a workspace keeps, `invalid` for one that could not be read and `empty` for one that held nothing.

Scopes: `contacts:read`.

**Parameters**

- `id` (`str`, required): Contacts import id, `iimp_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactImportResource` with `status`, `counts`, `report`, `undoable` and when it started, finished and was undone.

**Example**

```python
from nextmail import nextmail

item = nextmail.contacts.get_import('iimp_7b1e4d8f60a5c7b92d3f9c2a')
counts = item['counts']

print(item['status'], counts['imported'], counts['merged'], counts['skipped'])

for sample in item['report']['samples']:
    print(sample['name'], sample['reason'])
```

**Notes**

- An id that names no contacts import is a 404 `item_import_not_found`, which raises `NextmailApiError` with `is_not_found`.
- A GET is retried automatically on network failure and on 408, 500, 502, 503 and 504 responses, up to the client's `max_retries`, and on a 429 only when it carries a `Retry-After` of a minute or less.

Also available in: API [`GET /contacts/imports/{id}`](https://nextmail.sh/docs/api/reference/contacts#get-contacts-imports-id); TypeScript [`nextmail.contacts.getImport()`](https://nextmail.sh/docs/sdk/reference/contacts#getImport); Ruby [`contacts.get_import`](https://nextmail.sh/docs/ruby/reference/contacts#getImport); PHP [`contacts->getImport`](https://nextmail.sh/docs/php/reference/contacts#getImport); Go [`Contacts.GetImport`](https://nextmail.sh/docs/go/reference/contacts#getImport); Java [`contacts().getImport`](https://nextmail.sh/docs/java/reference/contacts#getImport); C# [`Contacts.GetImportAsync`](https://nextmail.sh/docs/csharp/reference/contacts#getImport); CLI [`nextmail contacts get-import`](https://nextmail.sh/docs/cli/reference/contacts#contacts-get-import).

### `contacts.undo_import()`

Remove what a contacts import added

```python
def undo_import(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> ContactImportResource
```

Deletes every contact and card the import added, changed since or not, and marks the import `undone`. Contacts that were already in the address book are not touched.

Scopes: `contacts:write`.

**Parameters**

- `id` (`str`, required): Contacts import id, `iimp_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`ContactImportResource` with `status` set to `undone` and `undoneAt` set.

**Example**

```python
from nextmail import nextmail

undone = nextmail.contacts.undo_import('iimp_7b1e4d8f60a5c7b92d3f9c2a')

print(undone['status'], undone['undoneAt'], undone['counts']['imported'])
```

**Notes**

- An import that is still running, or was already removed, is a 409 `item_import_not_undoable`. `undoable` on the import says whether the call would remove anything.
- An id that names no contacts import is a 404 `item_import_not_found`.
- Not retried automatically by the SDK.

Also available in: API [`POST /contacts/imports/{id}/undo`](https://nextmail.sh/docs/api/reference/contacts#post-contacts-imports-id-undo); TypeScript [`nextmail.contacts.undoImport()`](https://nextmail.sh/docs/sdk/reference/contacts#undoImport); Ruby [`contacts.undo_import`](https://nextmail.sh/docs/ruby/reference/contacts#undoImport); PHP [`contacts->undoImport`](https://nextmail.sh/docs/php/reference/contacts#undoImport); Go [`Contacts.UndoImport`](https://nextmail.sh/docs/go/reference/contacts#undoImport); Java [`contacts().undoImport`](https://nextmail.sh/docs/java/reference/contacts#undoImport); C# [`Contacts.UndoImportAsync`](https://nextmail.sh/docs/csharp/reference/contacts#undoImport); CLI [`nextmail contacts undo-import`](https://nextmail.sh/docs/cli/reference/contacts#contacts-undo-import).
