# How to create and update contacts

Source: https://developer.nylas.com/docs/cookbook/contacts/create-and-update-contacts/

A sales rep finishes a call and you want to write the new lead straight into their Gmail address book, or a meeting wraps and you need to push an attendee into their Outlook contacts so it shows up everywhere they read mail. Writing to each provider natively means juggling Google's People API and Microsoft Graph, with two auth models and two field schemas. The Nylas Contacts API gives you one create, update, and delete flow that writes to a Gmail or Outlook address book through the same request.

This recipe walks through the three write operations and the provider rules that decide whether a write sticks.

## Create a contact

Create a contact with a single `POST` to `/v3/grants/{grant_id}/contacts`. The body accepts 17 contact fields, and only `given_name` is required. Nylas writes the record into the user's address book and returns the full contact with its new `id`, so you can store that ID and update or delete the contact later.

The request below sends a complete contact: name parts, two emails, two phone numbers, a birthday, groups, and a `source` of `address_book`. Every field except `given_name` is optional, so a minimal create can be just a name plus one email. The response echoes the stored object with a server-assigned `id`.

```bash
curl --compressed --request POST \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "birthday": "1960-12-31",
    "company_name": "Nylas",
    "emails": [
      {
        "email": "leyah.miller@example.com",
        "type": "work"
      },
      {
        "email": "leyah@example.com",
        "type": "home"
      }
    ],
    "given_name": "Leyah",
    "groups": [
      {
        "id": "starred"
      },
      {
        "id": "friends"
      }
    ],
    "im_addresses": [
      {
        "type": "jabber",
        "im_address": "leyah_jabber"
      },
      {
        "type": "msn",
        "im_address": "leyah_msn"
      }
    ],
    "job_title": "Software Engineer",
    "manager_name": "Bill",
    "middle_name": "Allison",
    "nickname": "Allie",
    "notes": "Loves Ramen",
    "office_location": "123 Main Street",
    "phone_numbers": [
      {
        "number": "+1-555-555-5555",
        "type": "work"
      },
      {
        "number": "+1-555-555-5556",
        "type": "home"
      }
    ],
    "physical_addresses": [
      {
        "type": "work",
        "street_address": "123 Main Street",
        "postal_code": "94107",
        "state": "CA",
        "country": "USA",
        "city": "San Francisco"
      },
      {
        "type": "home",
        "street_address": "456 Main Street",
        "postal_code": "94107",
        "state": "CA",
        "country": "USA",
        "city": "San Francisco"
      }
    ],
    "source": "address_book",
    "surname": "Miller",
    "web_pages": [
      {
        "type": "work",
        "url": "<WEBPAGE_URL>"
      },
      {
        "type": "home",
        "url": "<WEBPAGE_URL>"
      }
    ]
  }'

```

```json
{
  "request_id": "1",
  "data": {
    "birthday": "1960-12-31",
    "company_name": "Nylas",
    "emails": [
      {
        "type": "work",
        "email": "leyah.miller@example.com"
      },
      {
        "type": "home",
        "email": "leyah@example.com"
      }
    ],
    "given_name": "Leyah",
    "grant_id": "<NYLAS_GRANT_ID>",
    "groups": [{ "id": "starred" }, { "id": "friends" }],
    "id": "<CONTACT_ID>",
    "im_addresses": [
      {
        "type": "jabber",
        "im_address": "leyah_jabber"
      },
      {
        "type": "msn",
        "im_address": "leyah_msn"
      }
    ],
    "job_title": "Software Engineer",
    "manager_name": "Bill",
    "middle_name": "Allison",
    "nickname": "Allie",
    "notes": "Loves ramen",
    "object": "contact",
    "office_location": "123 Main Street",
    "phone_numbers": [
      {
        "type": "work",
        "number": "+1-555-555-5555"
      },
      {
        "type": "home",
        "number": "+1-555-555-5556"
      }
    ],
    "physical_addresses": [
      {
        "type": "work",
        "street_address": "123 Main Street",
        "postal_code": "94107",
        "state": "CA",
        "country": "US",
        "city": "San Francisco"
      },
      {
        "type": "home",
        "street_address": "321 Pleasant Drive",
        "postal_code": "94107",
        "state": "CA",
        "country": "US",
        "city": "San Francisco"
      }
    ],
    "picture_url": "https://example.com/picture.jpg",
    "source": "address_book",
    "surname": "Miller",
    "web_pages": [
      {
        "type": "work",
        "url": "<WEBPAGE_URL>"
      },
      {
        "type": "home",
        "url": "<WEBPAGE_URL>"
      }
    ]
  }
}


```

```js [createContact-Node.js]

import Nylas from "nylas";

const nylas = new Nylas({
  apiKey: "<NYLAS_API_KEY>",
  apiUri: "<NYLAS_API_URI>",
});

async function createContact() {
  try {
    const contact = await nylas.contacts.create({
      identifier: "<NYLAS_GRANT_ID>",
      requestBody: {
        givenName: "My",
        middleName: "Nylas",
        surname: "Friend",
        notes: "Make sure to keep in touch!",
        emails: [{ type: "work", email: "swag@example.com" }],
        phoneNumbers: [{ type: "work", number: "(555) 555-5555" }],
        webPages: [{ type: "other", url: "nylas.com" }],
      },
    });

    console.log("Contact:", JSON.stringify(contact));
  } catch (error) {
    console.error("Error to create contact:", error);
  }
}

createContact();


```

```python
from nylas import Client

nylas = Client(
    "<NYLAS_API_KEY>",
    "<NYLAS_API_URI>"
)

grant_id = "<NYLAS_GRANT_ID>"

contact = nylas.contacts.create(
  grant_id,
  request_body={
    "middle_name": "Nylas",
    "surname": "Friend",
    "notes": "Make sure to keep in touch!",
    "emails": [{"type": "work", "email": "swag@example.com"}],
    "phone_numbers": [{"type": "work", "number": "(555) 555-5555"}],
    "web_pages": [{"type": "other", "url": "nylas.com"}]
  }
)

print(contact)

```

Writes land in the address book on Google, Microsoft, iCloud, IMAP, and EWS accounts. Set `source` to `address_book` so the contact is saved and editable; you can't create a contact with `source` set to `inbox`, since those are read-only auto-generated records. The full field list lives in the [Contacts API reference](/docs/v3/email/contacts/).

## Update a contact

Update a contact with a `PUT` to `/v3/grants/{grant_id}/contacts/{contact_id}`, using the `id` you got back from the create call. One detail trips people up: `PUT` replaces the whole contact object, it doesn't patch single fields. Send the complete contact you want stored, because any field you leave out gets cleared on the provider.

The SDK examples below send a small body to keep them readable, but treat that as a pattern, not a partial update. To change one phone number, read the contact first, change that field in the object you got back, then send the entire object. The response returns the updated contact with the same `id`.

```bash
curl --compressed --request PUT \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts/<CONTACT_ID>' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "birthday": "1960-12-31",
    "company_name": "Nylas",
    "emails": [
      {
        "email": "leyah.miller@example.com",
        "type": "work"
      },
      {
        "email": "leyah@example.com",
        "type": "home"
      }
    ],
    "given_name": "Leyah",
    "groups": [
      {
        "id": "starred"
      },
      {
        "id": "all"
      }
    ],
    "im_addresses": [
      {
        "type": "jabber",
        "im_address": "leyah_jabber"
      },
      {
        "type": "msn",
        "im_address": "leyah_msn"
      }
    ],
    "job_title": "Software Engineer",
    "manager_name": "Bill",
    "middle_name": "Allison",
    "nickname": "Allie",
    "notes": "Loves Ramen",
    "office_location": "123 Main Street",
    "phone_numbers": [
      {
        "number": "+1-555-555-5555",
        "type": "work"
      },
      {
        "number": "+1-555-555-5556",
        "type": "home"
      }
    ],
    "physical_addresses": [
      {
        "type": "work",
        "street_address": "123 Main Street",
        "postal_code": "94107",
        "state": "CA",
        "country": "USA",
        "city": "San Francisco"
      },
      {
        "type": "home",
        "street_address": "456 Main Street",
        "postal_code": "94107",
        "state": "CA",
        "country": "USA",
        "city": "San Francisco"
      }
    ],
    "source": "address_book",
    "surname": "Miller",
    "web_pages": [
      {
        "type": "work",
        "url": "<WEBPAGE_URL>"
      },
      {
        "type": "home",
        "url": "<WEBPAGE_URL>"
      }
    ]
  }'

```

```js [updateContact-Node.js]

import Nylas from "nylas";

const nylas = new Nylas({
  apiKey: "<NYLAS_API_KEY>",
  apiUri: "<NYLAS_API_URI>",
});

async function updateContact() {
  try {
    const contact = await nylas.contacts.update({
      identifier: "<NYLAS_GRANT_ID>",
      contactId: "<CONTACT_ID>",
      requestBody: {
        givenName: "Nyla",
      },
    });

    console.log("Contact:", JSON.stringify(contact));
  } catch (error) {
    console.error("Error to create contact:", error);
  }
}

updateContact();


```

```python
from nylas import Client

nylas = Client(
    "<NYLAS_API_KEY>",
    "<NYLAS_API_URI>"
)

grant_id = "<NYLAS_GRANT_ID>"
contact_id = "<CONTACT_ID>"

contact = nylas.contacts.update(
  grant_id,
  contact_id,
  request_body={
    "given_name": "Nyla",
  }
)

print(contact)

```

Updates need the same write scope as create, and they only work on contacts whose `source` is `address_book`. Trying to edit an auto-generated `inbox` contact returns an error, so check the `source` field before you offer an edit action in your UI.

## Delete a contact

Delete a contact with a `DELETE` to `/v3/grants/{grant_id}/contacts/{contact_id}`. This is a single call with no request body, and it removes the contact from the user's actual provider account, not just from a Nylas cache. The contact disappears from Gmail or Outlook too, so treat it as a destructive action and confirm with the user first.

The examples below delete one contact by ID. A successful delete returns a `request_id` so you can correlate the call in logs. There's no soft-delete or trash step here, so once the call succeeds the record is gone from the provider.

```bash
curl --compressed --request DELETE \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/contacts/<CONTACT_ID>' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY>' \
  --header 'Content-Type: application/json'

```

```js [deleteContact-Node.js]

import Nylas from "nylas";

const nylas = new Nylas({
  apiKey: "<NYLAS_API_KEY>",
  apiUri: "<NYLAS_API_URI>",
});
const identifier = "<NYLAS_GRANT_ID>";
const contactId = "<CONTACT_ID>";

const deleteContact = async () => {
  try {
    await nylas.contacts.destroy({ identifier, contactId });
    console.log(`Contact with ID ${contactId} deleted successfully.`);
  } catch (error) {
    console.error(`Error deleting contact with ID ${contactId}:`, error);
  }
};

deleteContact();


```

```python
from nylas import Client

nylas = Client(
    "<NYLAS_API_KEY>",
    "<NYLAS_API_URI>"
)

grant_id = "<NYLAS_GRANT_ID>"
contact_id = "<CONTACT_ID>"

request = nylas.contacts.destroy(
  grant_id,
  contact_id,
)

print(request)

```

Like update, delete only applies to `address_book` contacts. Auto-generated `inbox` contacts aren't deletable through the API, because the provider regenerates them from message participants.

## Things to know about writing contacts

Writes behave differently from reads, and a handful of provider rules decide whether a `POST` or `PUT` actually sticks. The most important one is the `source` field, which Nylas recognizes in three values: `address_book`, `domain`, and `inbox`. Only `address_book` contacts are writable. The `inbox` source holds auto-created records built from message participants, and `domain` holds the organization directory, so both are read-only and reject create, update, and delete calls.

Write scopes are stricter than read scopes. Create, update, and delete all require Google's `https://www.googleapis.com/auth/contacts` scope or Microsoft's `Contacts.ReadWrite`. The read-only `contacts.readonly` or `Contacts.Read` scopes won't authorize a write, so request the read-write scope at OAuth time if your app edits contacts. The [Contacts API scopes](/docs/dev-guide/scopes/#contacts-api-scopes) page lists the exact strings per provider.

Field support varies by provider, and you'll hit these limits in practice:

| Field          | Google         | Microsoft / EWS                    | iCloud / IMAP        |
| -------------- | -------------- | ---------------------------------- | -------------------- |
| `groups`       | Multiple       | At most one group per contact      | At most one          |
| `web_pages`    | Multiple       | One, and `type` must be `work`     | At most one          |
| `suffix`       | Supported      | Dropped on EWS                     | Supported            |

Groups also work differently on write. You pass `groups` as an array of `{ "id": "..." }` objects, and the group has to already exist on the provider before you reference it. Microsoft, iCloud, and IMAP cap a contact at one group, so don't send several group IDs to those accounts. To find valid group IDs first, see [Organize contacts with groups](/docs/cookbook/contacts/contact-groups/). For the per-provider write rules straight from the source, Google documents its contact model in the [People API documentation](https://developers.google.com/people).

## What's next

- [Contacts API reference](/docs/v3/email/contacts/) for the full schema, field reference, and provider limits
- [How to list Google contacts](/docs/cookbook/contacts/list-contacts-google/) to read an address book before you write to it
- [Organize contacts with groups](/docs/cookbook/contacts/contact-groups/) to create and reference contact groups on write
- [Contacts API scopes](/docs/dev-guide/scopes/#contacts-api-scopes) for the read-write scope strings on each provider
- [API reference](/docs/reference/api/) for every Contacts endpoint and parameter