# Contacts

Manage Contacts and query for subscription lists, channels, named users, and attributes associated with a Contact.


## Contact association {#associatecontact}

Associate a channel, email address, or MSISDN with a Contact.  The `contact_id` is created if it does not already exist.

### `POST /api/contacts/associate`

{{< note >}}
If the `channel_id`, `email_address`, or `msisdn` is already associated with a `contact_id`, this operation will do nothing and return a 200 status code and the existing `contact_id`.
{{< /note >}}

**Security:**

- [oauth2Token]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-oauth2Token): cnt

**Request body**

Contact association requires a `channel_id`, `email_address`, or `msisdn`. Do not provide more than one identifier type in the same request. You can associate up to 100 Channel IDs to a Contact. Channel creation is asynchronous, so when associating a newly created channel, both a Channel ID and a device type are required to prevent "channel does not exist" errors.

**Content-Type:** `application/json`

**One of:**

  - **`channel_id`** `string` **REQUIRED**

    The Channel ID you want to associate with a Contact.

    Format: `uuid`

    Example: `9c36e8c7-5a73-47c0-9716-99fd3d4197d5`

  - **`contact_id`** `string` **REQUIRED**

    A string value identifying the user, without leading or trailing whitespace.

    Min length: 1, Max length: 128

    Example: `john_doe`

  - **`device_type`** `string`

    The device type of the channel.

    Possible values: `ios`, `android`, `amazon`, `web`, `open`, `email`, `sms`

  - **`contact_id`** `string` **REQUIRED**

    The existing Contact.

    Min length: 1, Max length: 128

    Example: `john_doe`

  - **`email_address`** `string` **REQUIRED**

    The email address you want to associate with a Contact.

    Format: `email`

    Example: `user@example.com`


**Responses**

**`200`** The request was accepted. The `contact_id` is returned in the response.

Response body:

**Content-Type:** `application/vnd.urbanairship+json; version=3`

[OK response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#okresponseobject)

**`400`** There was a parsing or validation error in the request. Bad Request errors typically include `path` and `location` in the response to help you find the cause of the error.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`401`** Authentication information (the app key and secret or bearer token) was either incorrect or missing.

Response body:

**Content-Type:** `text/plain`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`403`** Authentication was correct, but the user does not have permission to access the requested API, e.g., if the feature in question is not included in your pricing plan.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`406`** Return when the client requests a version of the API that cannot be satisfied, because no compatible version is currently deployed.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

---

## Contact disassociation {#disassociatecontact}

Disassociates channel from a Contact with the option of also opting out. You can identify a channel by MSISDN, email address, or Channel ID.

[Jump to examples ↓](#disassociatecontact-examples)

### `POST /api/contacts/disassociate/{contact_id}`

**Security:**

- [oauth2Token]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-oauth2Token): cnt

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contact_id` | `string` | Required | The Contact ID to disassociate from a channel. |

**Request body**

**Content-Type:** `application/json`

- **`channel_id`** `string`

  The Channel ID to disassociate from the Contact.

  Format: `uuid`

  Example: `{{channel_id}}`

- **`channel_type`** `string` **REQUIRED**

  The type of channel.

  Possible values: `sms`, `email`, `ios`, `android`, `amazon`, `web`, `open`

- **`email_address`** `string`

  The email address to disassociate from the Contact.

- **`msisdn`** `string`

  The mobile phone number to disassociate from the Contact. Must be numeric characters only, without leading zeros. 15 digits maximum.

  Max length: 15

- **`opt_out`** `boolean`

  Optional. True if the channel is to be opted out; otherwise false.

- **`sender`** `string`

  A long or short code the app is configured to send from. For example, `12345`.

**Responses**

**`200`** The request was accepted.

Response body:

**Content-Type:** `application/json`

- **`channel_id`** `string`

  Identifies the existing unique Channel ID.

  Format: `uuid`

  Example: `ef625038-70a3-41f1-826f-57bc11dd625a`

- **`contact`** `string`

  Identifies the existing unique Contact ID.

  Format: `uuid`

  Example: `ef625038-70a3-41f1-826f-57bc11dd625a`

- **`ok`** `boolean`

  Success.

**`400`** There was a parsing or validation error in the request. Bad Request errors typically include `path` and `location` in the response to help you find the cause of the error.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`401`** Authentication information (the app key and secret or bearer token) was either incorrect or missing.

Response body:

**Content-Type:** `text/plain`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**Examples**

*Example dissociate SMS channel from Contact by Channel ID*

```http
POST /api/contacts/disassociate/eed87e83-2f2f-4919-bcb0-8c620e0fae40 HTTP/1.1
Authorization: Bearer <contactJWTToken>
Accept: application/vnd.urbanairship+json; version=3
Content-Type: application/json

{
  "channel_type": "sms",
  "channel_id": "c8d58c64-7eb5-40ad-ad51-d39e05335c48"
}

```

*Example dissociate SMS channel from Contact by sender ID and MSISDN*

```http
POST /api/contacts/disassociate/eed87e83-2f2f-4919-bcb0-8c620e0fae40 HTTP/1.1
Authorization: Bearer <contactJWTToken>
Accept: application/vnd.urbanairship+json; version=3
Content-Type: application/json

{
  "channel_type": "sms",
  "sender": "1234",
  "msisdn": "15035551234"
}

```

*Example dissociate email channel from Contact by email address*

```http
POST /api/contacts/disassociate/eed87e83-2f2f-4919-bcb0-8c620e0fae40 HTTP/1.1
Authorization: Bearer <contactJWTToken>
Accept: application/vnd.urbanairship+json; version=3
Content-Type: application/json

{
  "channel_type": "email",
  "email_address": "abc@example.com"
}

```

*Example dissociate email channel from Contact by email address with opt_out*

```http
POST /api/contacts/disassociate/eed87e83-2f2f-4919-bcb0-8c620e0fae40 HTTP/1.1
Authorization: Bearer <contactJWTToken>
Accept: application/vnd.urbanairship+json; version=3
Content-Type: application/json

{
  "channel_type": "email",
  "email_address": "abc@example.com",
  "opt_out": true
}

```

*Example of a successful response*

```http
HTTP/1.1 200 OK
Content-Type: application/json

{
    "ok": true,
    "contact": "eed87e83-2f2f-4919-bcb0-8c620e0fae40",
    "channel_id": "c8d58c64-7eb5-40ad-ad51-d39e05335c48"
}

```

---

## Contacts tags {#modifycontacttags}

Add, remove, or set tags on a Contact. A single request body may contain add and/or remove objects or a single set field. At least one of the add, remove, or set objects must be present in a request.

### `POST /api/contacts/tags/`

{{< important >}}
A tag must be < 128 characters. A request with one or more tags longer than 128 characters will return a 400 response.
We support up to 1,000 tags per Contact. Adding more than 1,000 tags per Contact can cause latency and service interruptions. We strongly recommend removing unused tags whenever possible, and using Custom Events when appropriate. Please [contact Support](https://support.airship.com/) if you believe your use case requires more than 1,000 tags per Contact, and they will help you find an alternative.
{{< /important >}}

**Security:**

- [oauth2Token]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-oauth2Token): cnt

**Request body**

**Content-Type:** `application/json`

- **`add`** `object` <[Tag Group object]({{< ref "/developer/rest-api/ua/schemas/tags/" >}}#taggroupobject)>

  Add the list of tags to the Contacts, but do not remove any. If the tags are already present, they are not modified.

  Tags belong to Tag Groups. Tag Groups appear within the `tags` object for a Named User or the `tag_groups` object for a channel. See also [Device Properties](/docs/reference/data-collection/device-properties/)
``ua_`` is a reserved prefix for Airship-specific tag groups.

A Tag Group has two parts: a "name" string of 1-128 characters, and an array of tags, containing 0-100 tags. Each tag in the array is also a string consisting of 1-128 characters.

- **`audience`** `object` **REQUIRED**

  The Contacts you want to associate/disassociate tags with.

  **OBJECT PROPERTIES**

  - **`contact_id`** `array[string]`

    Min items: 1, Max items: 1000

- **`remove`** `object` <[Tag Group object]({{< ref "/developer/rest-api/ua/schemas/tags/" >}}#taggroupobject)>

  Remove the list of tags from the Contacts, but do not remove any others. If the tags are not currently present, nothing happens.

  Tags belong to Tag Groups. Tag Groups appear within the `tags` object for a Named User or the `tag_groups` object for a channel. See also [Device Properties](/docs/reference/data-collection/device-properties/)
``ua_`` is a reserved prefix for Airship-specific tag groups.

A Tag Group has two parts: a "name" string of 1-128 characters, and an array of tags, containing 0-100 tags. Each tag in the array is also a string consisting of 1-128 characters.

- **`set`** `object` <[Tag Group object]({{< ref "/developer/rest-api/ua/schemas/tags/" >}}#taggroupobject)>

  Set these tags for the audience. Any tags previously associated with the audience tags that are not in this current list are removed.

  Tags belong to Tag Groups. Tag Groups appear within the `tags` object for a Named User or the `tag_groups` object for a channel. See also [Device Properties](/docs/reference/data-collection/device-properties/)
``ua_`` is a reserved prefix for Airship-specific tag groups.

A Tag Group has two parts: a "name" string of 1-128 characters, and an array of tags, containing 0-100 tags. Each tag in the array is also a string consisting of 1-128 characters.

**Responses**

**`200`** If a tag request is partially valid, i.e., at least one tag group exists and is active, a 200 will be returned with a
warning in the response about the tag groups that failed to update. The tag groups listed in the warning will be CSV-formatted.


Response body:

**Content-Type:** `application/json`

- **`ok`** `boolean` **REQUIRED**

  Set to `true` when status code is `200`.


- **`tag_warnings`** `string`

  Warnings encountered when processing tags for this Contact.


**`400`** Parsing or validating the request failed. You will see this error if the same tag is present in both the add and remove fields.

Response body:

**Content-Type:** `application/vnd.urbanairship+json; version=3`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`401`** Authentication information (the app key and secret or bearer token) was either incorrect or missing.

Response body:

**Content-Type:** `text/plain`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`403`** Authentication was correct, but the user does not have permission to access the requested API, e.g., if the feature in question is not included in your pricing plan.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`406`** Return when the client requests a version of the API that cannot be satisfied, because no compatible version is currently deployed.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

---

## Look up Contact ID by Channel ID {#getcontactidfromchannelid}

Looks up a Contact ID for the given Channel ID.


[Jump to examples ↓](#getcontactidfromchannelid-examples)

### `GET /api/contacts/lookup/channel/{channel_id}`

**Security:**

- [basicAuth]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-basicAuth)
- [bearerAuth]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-bearerAuth)
- [oauth2Token]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-oauth2Token): cnt

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `channel_id` | `string` | Required | The Channel ID for which to retrieve the associated Contact ID. |

**Responses**

**`200`** The request was accepted.

Response body:

**Content-Type:** `application/json`

- **`contact_id`** `string` **REQUIRED**

  The resolved Contact ID.


  Format: `uuid`

- **`is_anonymous`** `boolean` **REQUIRED**

  Specifies whether the Contact is anonymous or not.


- **`ok`** `boolean` **REQUIRED**

  Set to `true` when status code is `200`.


**`400`** There was a parsing or validation error in the request. Bad Request errors typically include `path` and `location` in the response to help you find the cause of the error.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`401`** Authentication information (the app key and secret or bearer token) was either incorrect or missing.

Response body:

**Content-Type:** `text/plain`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`403`** Authentication was correct, but the user does not have permission to access the requested API, e.g., if the feature in question is not included in your pricing plan.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`404`** The requested resource doesn't exist.

Response body:

**Content-Type:** `application/vnd.urbanairship+json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`406`** Return when the client requests a version of the API that cannot be satisfied, because no compatible version is currently deployed.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**Examples**

*Example get Contact ID from a Channel ID
*

```http
GET /api/contacts/lookup/channel/164c3a13-9c75-4ea9-be2c-1bed2c97f9c3 HTTP/1.1
Authorization: Basic <application or master authorization string>
Accept: application/vnd.urbanairship+json; version=3

```

```http
HTTP/1.1 200 OK
Content-Type: application/vnd.urbanairship+json; version=3

{
   "ok": true,
   "contact_id": "7c24ebdd-ec06-47d4-9a56-ced8611f5b52",
   "is_anonymous": false
}

```

---

## Look up Contact ID by Named User ID {#getcontactidfromnameduserid}

Looks up a Contact ID for the given Named User ID.


[Jump to examples ↓](#getcontactidfromnameduserid-examples)

### `GET /api/contacts/lookup/named_user/{named_user_id}`

**Security:**

- [basicAuth]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-basicAuth)
- [bearerAuth]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-bearerAuth)
- [oauth2Token]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-oauth2Token): cnt

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `named_user_id` | `string` | Required | The URL-encoded Named User ID for which to retrieve the associated Contact ID. |

**Responses**

**`200`** The request was accepted.

Response body:

**Content-Type:** `application/json`

- **`contact_id`** `string` **REQUIRED**

  The resolved Contact ID.


  Format: `uuid`

- **`ok`** `boolean` **REQUIRED**

  Set to `true` when status code is `200`.


**`400`** There was a parsing or validation error in the request. Bad Request errors typically include `path` and `location` in the response to help you find the cause of the error.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`401`** Authentication information (the app key and secret or bearer token) was either incorrect or missing.

Response body:

**Content-Type:** `text/plain`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`403`** Authentication was correct, but the user does not have permission to access the requested API, e.g., if the feature in question is not included in your pricing plan.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`404`** The requested resource doesn't exist.

Response body:

**Content-Type:** `application/vnd.urbanairship+json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`406`** Return when the client requests a version of the API that cannot be satisfied, because no compatible version is currently deployed.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**Examples**

*Example get Contact ID from a Named User ID
*

```http
GET /api/contacts/lookup/named_user/90ae282f-f56e-4037-8174-482ef7e3e5f4 HTTP/1.1
Authorization: Basic <application or master authorization string>
Accept: application/vnd.urbanairship+json; version=3

```

```http
HTTP/1.1 200 OK
Content-Type: application/vnd.urbanairship+json; version=3

{
   "ok": true,
   "contact_id": "7c24ebdd-ec06-47d4-9a56-ced8611f5b52"
}

```

---

## Scoped Contact batch operations {#performscopedcontactbatchoperations}

Performs one or more scoped operations on a Contact in a single request. The body is a scoped array, and each item has a scope and one or more operations. 
Behavior matches the equivalent single-operation APIs. For each scope:
* Subscription list operations are supported. You can subscribe and/or unsubscribe the Contact to/from subscription lists for that scope, which is the same behavior as the subscription list APIs.
* Tag operations are not supported. If `tags` is present in any scoped item, the request is rejected with 400 and an error indicating that tags are not allowed in this context.

The following apply:
  * Set operations are not supported.
  * Failure to authorize on secure tags results in a 200 and warning.
  * Failure to find any valid tag groups results in a 200 and warning.
  * Failure to find any valid attributes results in a 200 and warning.


[Jump to examples ↓](#performscopedcontactbatchoperations-examples)

### `POST /api/contacts/scoped/{contact_id}`

{{< note >}}
If you are using a single-channel Preference Center created before October 10, 2022, that has not been [migrated to user-level](/docs/guides/messaging/features/preference-centers/#migrating-to-a-user-level-preference-center), use the [`/api/channels/subscription_lists` endpoint](/docs/developer/rest-api/ua/operations/channels/#modifychannelsubscriptions) to add or remove individual channels to/from your subscription list.

{{< /note >}}

{{< important >}}
The path parameter `contact_id` should be URL-encoded to ensure it is handled correctly.

{{< /important >}}

**Security:**

- [basicAppAuth]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-basicAppAuth)
- [bearerAuth]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-bearerAuth)
- [oauth2Token]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-oauth2Token): cnt

**Path parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contact_id` | `string` | Required | A string (UUID) value identifying the Contact with no leading or trailing whitespace.  |

**Request body**

**Content-Type:** `application/json`

- **`scoped`** `array` <[Contacts scoped batch item]({{< ref "/developer/rest-api/ua/schemas/contacts/" >}}#contactsscopedbatchitem)>

  An array of scopes, tags, and subscription lists.

**Responses**

**`200`** The request was accepted.


Response body:

**Content-Type:** `application/json`

- **`ok`** `boolean` **REQUIRED**

  Whether the operation was successful.

- **`subscription_list_warnings`** `array[string]`

  Any warnings related to subscription list operations.

- **`tag_warnings`** `array[string]`

  Any warnings related to tag operations.

**`400`** There was a parsing or validation error in the request. Bad Request errors typically include `path` and `location` in the response to help you find the cause of the error.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`401`** Authentication information (the app key and secret or bearer token) was either incorrect or missing.

Response body:

**Content-Type:** `text/plain`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`403`** Authentication was correct, but the user does not have permission to access the requested API, e.g., if the feature in question is not included in your pricing plan.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**`406`** Return when the client requests a version of the API that cannot be satisfied, because no compatible version is currently deployed.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

**Examples**

*Example scoped contact batch operations
*

```http
POST /api/contacts/scoped/77721a11-7ffa-4362-9bdb-f27ca891f9de HTTP/1.1
Authorization: Basic <application or master authorization string>
Accept: application/vnd.urbanairship+json; version=3
Content-Type: application/json

{
  "scoped": [{
    "scope": ["web", "email", "app"],
    "subscription_lists": {
      "subscribe": ["subscription_1", "subscription_2"],
      "unsubscribe": ["subscription_3"]
    }
  }]
}

```

```http
HTTP/1.1 200 OK
Content-Type: application/vnd.urbanairship+json; version=3

{
   "ok": true
}

```

---

## Set or remove attributes on a Contact {#modifycontactattributes}

Set or remove attributes on a Contact.

A single request body may contain a `set` or `remove` field, or both, or a single `set` field. If both `set` and `remove` fields are present and the intersection of the attributes in these fields is not empty, then a `400` will be returned.

If an attribute request is partially valid, i.e., at least one attribute exists, Airship returns a `200` with a warning about the attributes that failed to update.
The attributes listed in the `warnings` string will be in CSV format.


### `POST /api/contacts/attributes`

{{< note >}}
Airship returns a `200` with a warning if you attempt to set attributes on a Contact that does not exist.

{{< /note >}}

{{< tip >}}
If you wish to set attributes on multiple Contact at once, we recommend
using [/api/channels/attributes](/docs/developer/rest-api/ua/operations/channels/#modifychannelattributes) which
supports an `audience` object in the request body.

{{< /tip >}}

**Security:**

- [oauth2Token]({{< ref "/developer/rest-api/ua/introduction/" >}}#security-oauth2Token): cnt

**Request body**

**Content-Type:** `application/json`

Type: `array`

**Responses**

**`200`** Success. If an attribute request is partially valid, i.e., at least one attribute exists,
Airship returns a `200` with a warning string containing a CSV list of attributes that failed to update. Airship also returns a `200` with a warning if you attempt to set attributes on a `contact_id`, even if the `contact_id` does not exist.


Response body:

**Content-Type:** `application/json`

- **`ok`** `boolean`

  Set to `true` when status code is `200`.


- **`warnings`** `string`

  Warnings encountered when updating Contact attributes.


**`400`** There was a parsing or validation error in the request. Bad Request errors typically include `path` and `location` in the response to help you find the cause of the error.

Response body:

**Content-Type:** `application/json`

[Error response]({{< ref "/developer/rest-api/ua/schemas/responses/" >}}#error)

---

