# This file was auto-generated by Fern from our API Definition.

import typing
from json.decoder import JSONDecodeError

from ..companies.types.company import Company
from ..core.api_error import ApiError
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.http_response import AsyncHttpResponse, HttpResponse
from ..core.jsonable_encoder import jsonable_encoder
from ..core.pagination import AsyncPager, SyncPager
from ..core.request_options import RequestOptions
from ..core.serialization import convert_and_respect_annotation_metadata
from ..core.unchecked_base_model import construct_type
from ..errors.not_found_error import NotFoundError
from ..errors.unauthorized_error import UnauthorizedError
from ..subscription_types.types.subscription_type import SubscriptionType
from ..types.contact_archived import ContactArchived
from ..types.contact_attached_companies import ContactAttachedCompanies
from ..types.contact_blocked import ContactBlocked
from ..types.contact_deleted import ContactDeleted
from ..types.contact_list import ContactList
from ..types.contact_segments import ContactSegments
from ..types.contact_unarchived import ContactUnarchived
from ..types.create_contact_request import CreateContactRequest
from ..types.error import Error
from ..types.search_request_query import SearchRequestQuery
from ..types.starting_after_paging import StartingAfterPaging
from ..types.subscription_type_list import SubscriptionTypeList
from ..types.tag_list import TagList
from .types.contact import Contact
from .types.contacts_create_response import ContactsCreateResponse
from .types.contacts_find_response import ContactsFindResponse
from .types.contacts_merge_lead_in_user_response import ContactsMergeLeadInUserResponse
from .types.contacts_update_response import ContactsUpdateResponse
from .types.show_contact_by_external_id_response import ShowContactByExternalIdResponse

# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)


class RawContactsClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._client_wrapper = client_wrapper

    def list_attached_companies(
        self,
        contact_id: str,
        *,
        page: typing.Optional[int] = None,
        per_page: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> SyncPager[Company, ContactAttachedCompanies]:
        """
        You can fetch a list of companies that are associated to a contact.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        page : typing.Optional[int]
            The page of results to fetch. Defaults to first page

        per_page : typing.Optional[int]
            How many results to display per page. Defaults to 15

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        SyncPager[Company, ContactAttachedCompanies]
            successful
        """
        page = page if page is not None else 1

        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/companies",
            method="GET",
            params={
                "page": page,
                "per_page": per_page,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ContactAttachedCompanies,
                    construct_type(
                        type_=ContactAttachedCompanies,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _items = _parsed_response.companies
                _has_next = True
                _get_next = lambda: self.list_attached_companies(
                    contact_id,
                    page=page + 1,
                    per_page=per_page,
                    request_options=request_options,
                )
                return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def list_attached_segments(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ContactSegments]:
        """
        You can fetch a list of segments that are associated to a contact.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ContactSegments]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/segments",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactSegments,
                    construct_type(
                        type_=ContactSegments,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def list_attached_subscriptions(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[SubscriptionTypeList]:
        """
        You can fetch a list of subscription types that are attached to a contact. These can be subscriptions that a user has 'opted-in' to or has 'opted-out' from, depending on the subscription type.
        This will return a list of Subscription Type objects that the contact is associated with.

        The data property will show a combined list of:

          1.Opt-out subscription types that the user has opted-out from.
          2.Opt-in subscription types that the user has opted-in to receiving.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[SubscriptionTypeList]
            Successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/subscriptions",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    SubscriptionTypeList,
                    construct_type(
                        type_=SubscriptionTypeList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def attach_subscription(
        self,
        contact_id: str,
        *,
        subscription_id: str,
        consent_type: str,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[SubscriptionType]:
        """
        You can add a specific subscription to a contact. In Intercom, we have two different subscription types based on user consent - opt-out and opt-in:

          1.Attaching a contact to an opt-out subscription type will opt that user out from receiving messages related to that subscription type.

          2.Attaching a contact to an opt-in subscription type will opt that user in to receiving messages related to that subscription type.

        This will return a subscription type model for the subscription type that was added to the contact.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        subscription_id : str
            The unique identifier for the subscription which is given by Intercom

        consent_type : str
            The consent_type of a subscription, opt_out or opt_in.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[SubscriptionType]
            Successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/subscriptions",
            method="POST",
            json={
                "id": subscription_id,
                "consent_type": consent_type,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    SubscriptionType,
                    construct_type(
                        type_=SubscriptionType,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def detach_subscription(
        self, contact_id: str, subscription_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[SubscriptionType]:
        """
        You can remove a specific subscription from a contact. This will return a subscription type model for the subscription type that was removed from the contact.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        subscription_id : str
            The unique identifier for the subscription type which is given by Intercom

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[SubscriptionType]
            Successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/subscriptions/{jsonable_encoder(subscription_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    SubscriptionType,
                    construct_type(
                        type_=SubscriptionType,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def list_attached_tags(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[TagList]:
        """
        You can fetch a list of all tags that are attached to a specific contact.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[TagList]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/tags",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TagList,
                    construct_type(
                        type_=TagList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def find(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ContactsFindResponse]:
        """
        You can fetch the details of a single contact.

        Parameters
        ----------
        contact_id : str
            contact_id

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ContactsFindResponse]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactsFindResponse,
                    construct_type(
                        type_=ContactsFindResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def update(
        self,
        contact_id: str,
        *,
        role: typing.Optional[str] = OMIT,
        external_id: typing.Optional[str] = OMIT,
        email: typing.Optional[str] = OMIT,
        phone: typing.Optional[str] = OMIT,
        name: typing.Optional[str] = OMIT,
        avatar: typing.Optional[str] = OMIT,
        signed_up_at: typing.Optional[int] = OMIT,
        last_seen_at: typing.Optional[int] = OMIT,
        owner_id: typing.Optional[int] = OMIT,
        unsubscribed_from_emails: typing.Optional[bool] = OMIT,
        custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[ContactsUpdateResponse]:
        """
        You can update an existing contact (ie. user or lead).

        {% admonition type="info" %}
          This endpoint handles both **contact updates** and **custom object associations**.

          See _`update a contact with an association to a custom object instance`_ in the request/response examples to see the custom object association format.
        {% /admonition %}

        Parameters
        ----------
        contact_id : str
            id

        role : typing.Optional[str]
            The role of the contact.

        external_id : typing.Optional[str]
            A unique identifier for the contact which is given to Intercom

        email : typing.Optional[str]
            The contacts email

        phone : typing.Optional[str]
            The contacts phone

        name : typing.Optional[str]
            The contacts name

        avatar : typing.Optional[str]
            An image URL containing the avatar of a contact

        signed_up_at : typing.Optional[int]
            The time specified for when a contact signed up

        last_seen_at : typing.Optional[int]
            The time when the contact was last seen (either where the Intercom Messenger was installed or when specified manually)

        owner_id : typing.Optional[int]
            The id of an admin that has been assigned account ownership of the contact

        unsubscribed_from_emails : typing.Optional[bool]
            Whether the contact is unsubscribed from emails

        custom_attributes : typing.Optional[typing.Dict[str, typing.Any]]
            The custom attributes which are set for the contact

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ContactsUpdateResponse]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}",
            method="PUT",
            json={
                "role": role,
                "external_id": external_id,
                "email": email,
                "phone": phone,
                "name": name,
                "avatar": avatar,
                "signed_up_at": signed_up_at,
                "last_seen_at": last_seen_at,
                "owner_id": owner_id,
                "unsubscribed_from_emails": unsubscribed_from_emails,
                "custom_attributes": custom_attributes,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactsUpdateResponse,
                    construct_type(
                        type_=ContactsUpdateResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def delete(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ContactDeleted]:
        """
        You can delete a single contact.

        Parameters
        ----------
        contact_id : str
            contact_id

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ContactDeleted]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactDeleted,
                    construct_type(
                        type_=ContactDeleted,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def merge_lead_in_user(
        self,
        *,
        lead_id: typing.Optional[str] = OMIT,
        contact_id: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[ContactsMergeLeadInUserResponse]:
        """
        You can merge a contact with a `role` of `lead` into a contact with a `role` of `user`.

        Parameters
        ----------
        lead_id : typing.Optional[str]
            The unique identifier for the contact to merge away from. Must be a lead.

        contact_id : typing.Optional[str]
            The unique identifier for the contact to merge into. Must be a user.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ContactsMergeLeadInUserResponse]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            "contacts/merge",
            method="POST",
            json={
                "from": lead_id,
                "into": contact_id,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactsMergeLeadInUserResponse,
                    construct_type(
                        type_=ContactsMergeLeadInUserResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def search(
        self,
        *,
        query: SearchRequestQuery,
        pagination: typing.Optional[StartingAfterPaging] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> SyncPager[Contact, ContactList]:
        """
        You can search for multiple contacts by the value of their attributes in order to fetch exactly who you want.

        To search for contacts, you need to send a `POST` request to `https://api.intercom.io/contacts/search`.

        This will accept a query object in the body which will define your filters in order to search for contacts.

        {% admonition type="warning" name="Optimizing search queries" %}
          Search queries can be complex, so optimizing them can help the performance of your search.
          Use the `AND` and `OR` operators to combine multiple filters to get the exact results you need and utilize
          pagination to limit the number of results returned. The default is `50` results per page.
          See the [pagination section](https://developers.intercom.com/docs/build-an-integration/learn-more/rest-apis/pagination/#example-search-conversations-request) for more details on how to use the `starting_after` param.
        {% /admonition %}
        ### Contact Creation Delay

        If a contact has recently been created, there is a possibility that it will not yet be available when searching. This means that it may not appear in the response. This delay can take a few minutes. If you need to be instantly notified it is recommended to use webhooks and iterate to see if they match your search filters.

        ### Nesting & Limitations

        You can nest these filters in order to get even more granular insights that pinpoint exactly what you need. Example: (1 OR 2) AND (3 OR 4).
        There are some limitations to the amount of multiple's there can be:
        * There's a limit of max 2 nested filters
        * There's a limit of max 15 filters for each AND or OR group

        ### Searching for Timestamp Fields

        All timestamp fields (created_at, updated_at etc.) are indexed as Dates for Contact Search queries; Datetime queries are not currently supported. This means you can only query for timestamp fields by day - not hour, minute or second.
        For example, if you search for all Contacts with a created_at value greater (>) than 1577869200 (the UNIX timestamp for January 1st, 2020 9:00 AM), that will be interpreted as 1577836800 (January 1st, 2020 12:00 AM). The search results will then include Contacts created from January 2nd, 2020 12:00 AM onwards.
        If you'd like to get contacts created on January 1st, 2020 you should search with a created_at value equal (=) to 1577836800 (January 1st, 2020 12:00 AM).
        This behaviour applies only to timestamps used in search queries. The search results will still contain the full UNIX timestamp and be sorted accordingly.

        ### Accepted Fields

        Most key listed as part of the Contacts Model are searchable, whether writeable or not. The value you search for has to match the accepted type, otherwise the query will fail (ie. as `created_at` accepts a date, the `value` cannot be a string such as `"foorbar"`).

        | Field                              | Type                           |
        | ---------------------------------- | ------------------------------ |
        | id                                 | String                         |
        | role                               | String<br>Accepts user or lead |
        | name                               | String                         |
        | avatar                             | String                         |
        | owner_id                           | Integer                        |
        | email                              | String                         |
        | email_domain                       | String                         |
        | phone                              | String                         |
        | external_id                        | String                         |
        | created_at                         | Date (UNIX Timestamp)          |
        | signed_up_at                       | Date (UNIX Timestamp)          |
        | updated_at                         | Date (UNIX Timestamp)          |
        | last_seen_at                       | Date (UNIX Timestamp)          |
        | last_contacted_at                  | Date (UNIX Timestamp)          |
        | last_replied_at                    | Date (UNIX Timestamp)          |
        | last_email_opened_at               | Date (UNIX Timestamp)          |
        | last_email_clicked_at              | Date (UNIX Timestamp)          |
        | language_override                  | String                         |
        | browser                            | String                         |
        | browser_language                   | String                         |
        | os                                 | String                         |
        | location.country                   | String                         |
        | location.region                    | String                         |
        | location.city                      | String                         |
        | unsubscribed_from_emails           | Boolean                        |
        | marked_email_as_spam               | Boolean                        |
        | has_hard_bounced                   | Boolean                        |
        | ios_last_seen_at                   | Date (UNIX Timestamp)          |
        | ios_app_version                    | String                         |
        | ios_device                         | String                         |
        | ios_app_device                     | String                         |
        | ios_os_version                     | String                         |
        | ios_app_name                       | String                         |
        | ios_sdk_version                    | String                         |
        | android_last_seen_at               | Date (UNIX Timestamp)          |
        | android_app_version                | String                         |
        | android_device                     | String                         |
        | android_app_name                   | String                         |
        | andoid_sdk_version                 | String                         |
        | segment_id                         | String                         |
        | tag_id                             | String                         |
        | custom_attributes.{attribute_name} | String                         |

        ### Accepted Operators

        {% admonition type="warning" name="Searching based on `created_at`" %}
          You cannot use the `<=` or `>=` operators to search by `created_at`.
        {% /admonition %}

        The table below shows the operators you can use to define how you want to search for the value.  The operator should be put in as a string (`"="`). The operator has to be compatible with the field's type (eg. you cannot search with `>` for a given string value as it's only compatible for integer's and dates).

        | Operator | Valid Types                      | Description                                                      |
        | :------- | :------------------------------- | :--------------------------------------------------------------- |
        | =        | All                              | Equals                                                           |
        | !=       | All                              | Doesn't Equal                                                    |
        | IN       | All                              | In<br>Shortcut for `OR` queries<br>Values must be in Array       |
        | NIN      | All                              | Not In<br>Shortcut for `OR !` queries<br>Values must be in Array |
        | >        | Integer<br>Date (UNIX Timestamp) | Greater than                                                     |
        | <       | Integer<br>Date (UNIX Timestamp) | Lower than                                                       |
        | ~        | String                           | Contains                                                         |
        | !~       | String                           | Doesn't Contain                                                  |
        | ^        | String                           | Starts With                                                      |
        | $        | String                           | Ends With                                                        |

        Parameters
        ----------
        query : SearchRequestQuery

        pagination : typing.Optional[StartingAfterPaging]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        SyncPager[Contact, ContactList]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            "contacts/search",
            method="POST",
            json={
                "query": convert_and_respect_annotation_metadata(
                    object_=query, annotation=SearchRequestQuery, direction="write"
                ),
                "pagination": convert_and_respect_annotation_metadata(
                    object_=pagination, annotation=StartingAfterPaging, direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ContactList,
                    construct_type(
                        type_=ContactList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _items = _parsed_response.data
                _has_next = False
                _get_next = None
                if _parsed_response.pages is not None and _parsed_response.pages.next is not None:
                    _parsed_next = _parsed_response.pages.next.starting_after
                    _has_next = _parsed_next is not None and _parsed_next != ""
                    _get_next = lambda: self.search(
                        query=query,
                        pagination=pagination,
                        request_options=request_options,
                    )
                return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def list(
        self,
        *,
        page: typing.Optional[int] = None,
        per_page: typing.Optional[int] = None,
        starting_after: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> SyncPager[Contact, ContactList]:
        """
        You can fetch a list of all contacts (ie. users or leads) in your workspace.
        {% admonition type="warning" name="Pagination" %}
          You can use pagination to limit the number of results returned. The default is `50` results per page.
          See the [pagination section](https://developers.intercom.com/docs/build-an-integration/learn-more/rest-apis/pagination/#pagination-for-list-apis) for more details on how to use the `starting_after` param.
        {% /admonition %}

        Parameters
        ----------
        page : typing.Optional[int]
            The page of results to fetch. Defaults to first page

        per_page : typing.Optional[int]
            How many results to display per page. Defaults to 15

        starting_after : typing.Optional[str]
            String used to get the next page of conversations.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        SyncPager[Contact, ContactList]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            "contacts",
            method="GET",
            params={
                "page": page,
                "per_page": per_page,
                "starting_after": starting_after,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ContactList,
                    construct_type(
                        type_=ContactList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _items = _parsed_response.data
                _has_next = False
                _get_next = None
                if _parsed_response.pages is not None and _parsed_response.pages.next is not None:
                    _parsed_next = _parsed_response.pages.next.starting_after
                    _has_next = _parsed_next is not None and _parsed_next != ""
                    _get_next = lambda: self.list(
                        page=page,
                        per_page=per_page,
                        starting_after=_parsed_next,
                        request_options=request_options,
                    )
                return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def create(
        self, *, request: CreateContactRequest, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ContactsCreateResponse]:
        """
        You can create a new contact (ie. user or lead).

        Parameters
        ----------
        request : CreateContactRequest

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ContactsCreateResponse]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            "contacts",
            method="POST",
            json=convert_and_respect_annotation_metadata(
                object_=request, annotation=CreateContactRequest, direction="write"
            ),
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactsCreateResponse,
                    construct_type(
                        type_=ContactsCreateResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def show_contact_by_external_id(
        self, external_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ShowContactByExternalIdResponse]:
        """
        You can fetch the details of a single contact by external ID. Note that this endpoint only supports users and not leads.

        Parameters
        ----------
        external_id : str
            The external ID of the user that you want to retrieve

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ShowContactByExternalIdResponse]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/find_by_external_id/{jsonable_encoder(external_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ShowContactByExternalIdResponse,
                    construct_type(
                        type_=ShowContactByExternalIdResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def archive(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ContactArchived]:
        """
        You can archive a single contact.

        Parameters
        ----------
        contact_id : str
            contact_id

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ContactArchived]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/archive",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactArchived,
                    construct_type(
                        type_=ContactArchived,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def unarchive(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ContactUnarchived]:
        """
        You can unarchive a single contact.

        Parameters
        ----------
        contact_id : str
            contact_id

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ContactUnarchived]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/unarchive",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactUnarchived,
                    construct_type(
                        type_=ContactUnarchived,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def block_contact(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> HttpResponse[ContactBlocked]:
        """
        Block a single contact.<br>**Note:** conversations of the contact will also be archived during the process.<br>More details in [FAQ How do I block Inbox spam?](https://www.intercom.com/help/en/articles/8838656-inbox-faqs)

        Parameters
        ----------
        contact_id : str
            contact_id

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[ContactBlocked]
            successful
        """
        _response = self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/block",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactBlocked,
                    construct_type(
                        type_=ContactBlocked,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)


class AsyncRawContactsClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    async def list_attached_companies(
        self,
        contact_id: str,
        *,
        page: typing.Optional[int] = None,
        per_page: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncPager[Company, ContactAttachedCompanies]:
        """
        You can fetch a list of companies that are associated to a contact.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        page : typing.Optional[int]
            The page of results to fetch. Defaults to first page

        per_page : typing.Optional[int]
            How many results to display per page. Defaults to 15

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncPager[Company, ContactAttachedCompanies]
            successful
        """
        page = page if page is not None else 1

        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/companies",
            method="GET",
            params={
                "page": page,
                "per_page": per_page,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ContactAttachedCompanies,
                    construct_type(
                        type_=ContactAttachedCompanies,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _items = _parsed_response.companies
                _has_next = True

                async def _get_next():
                    return await self.list_attached_companies(
                        contact_id,
                        page=page + 1,
                        per_page=per_page,
                        request_options=request_options,
                    )

                return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def list_attached_segments(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ContactSegments]:
        """
        You can fetch a list of segments that are associated to a contact.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ContactSegments]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/segments",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactSegments,
                    construct_type(
                        type_=ContactSegments,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def list_attached_subscriptions(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[SubscriptionTypeList]:
        """
        You can fetch a list of subscription types that are attached to a contact. These can be subscriptions that a user has 'opted-in' to or has 'opted-out' from, depending on the subscription type.
        This will return a list of Subscription Type objects that the contact is associated with.

        The data property will show a combined list of:

          1.Opt-out subscription types that the user has opted-out from.
          2.Opt-in subscription types that the user has opted-in to receiving.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[SubscriptionTypeList]
            Successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/subscriptions",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    SubscriptionTypeList,
                    construct_type(
                        type_=SubscriptionTypeList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def attach_subscription(
        self,
        contact_id: str,
        *,
        subscription_id: str,
        consent_type: str,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[SubscriptionType]:
        """
        You can add a specific subscription to a contact. In Intercom, we have two different subscription types based on user consent - opt-out and opt-in:

          1.Attaching a contact to an opt-out subscription type will opt that user out from receiving messages related to that subscription type.

          2.Attaching a contact to an opt-in subscription type will opt that user in to receiving messages related to that subscription type.

        This will return a subscription type model for the subscription type that was added to the contact.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        subscription_id : str
            The unique identifier for the subscription which is given by Intercom

        consent_type : str
            The consent_type of a subscription, opt_out or opt_in.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[SubscriptionType]
            Successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/subscriptions",
            method="POST",
            json={
                "id": subscription_id,
                "consent_type": consent_type,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    SubscriptionType,
                    construct_type(
                        type_=SubscriptionType,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def detach_subscription(
        self, contact_id: str, subscription_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[SubscriptionType]:
        """
        You can remove a specific subscription from a contact. This will return a subscription type model for the subscription type that was removed from the contact.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        subscription_id : str
            The unique identifier for the subscription type which is given by Intercom

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[SubscriptionType]
            Successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/subscriptions/{jsonable_encoder(subscription_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    SubscriptionType,
                    construct_type(
                        type_=SubscriptionType,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def list_attached_tags(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[TagList]:
        """
        You can fetch a list of all tags that are attached to a specific contact.

        Parameters
        ----------
        contact_id : str
            The unique identifier for the contact which is given by Intercom

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[TagList]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/tags",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TagList,
                    construct_type(
                        type_=TagList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        typing.Any,
                        construct_type(
                            type_=typing.Any,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def find(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ContactsFindResponse]:
        """
        You can fetch the details of a single contact.

        Parameters
        ----------
        contact_id : str
            contact_id

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ContactsFindResponse]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactsFindResponse,
                    construct_type(
                        type_=ContactsFindResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def update(
        self,
        contact_id: str,
        *,
        role: typing.Optional[str] = OMIT,
        external_id: typing.Optional[str] = OMIT,
        email: typing.Optional[str] = OMIT,
        phone: typing.Optional[str] = OMIT,
        name: typing.Optional[str] = OMIT,
        avatar: typing.Optional[str] = OMIT,
        signed_up_at: typing.Optional[int] = OMIT,
        last_seen_at: typing.Optional[int] = OMIT,
        owner_id: typing.Optional[int] = OMIT,
        unsubscribed_from_emails: typing.Optional[bool] = OMIT,
        custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[ContactsUpdateResponse]:
        """
        You can update an existing contact (ie. user or lead).

        {% admonition type="info" %}
          This endpoint handles both **contact updates** and **custom object associations**.

          See _`update a contact with an association to a custom object instance`_ in the request/response examples to see the custom object association format.
        {% /admonition %}

        Parameters
        ----------
        contact_id : str
            id

        role : typing.Optional[str]
            The role of the contact.

        external_id : typing.Optional[str]
            A unique identifier for the contact which is given to Intercom

        email : typing.Optional[str]
            The contacts email

        phone : typing.Optional[str]
            The contacts phone

        name : typing.Optional[str]
            The contacts name

        avatar : typing.Optional[str]
            An image URL containing the avatar of a contact

        signed_up_at : typing.Optional[int]
            The time specified for when a contact signed up

        last_seen_at : typing.Optional[int]
            The time when the contact was last seen (either where the Intercom Messenger was installed or when specified manually)

        owner_id : typing.Optional[int]
            The id of an admin that has been assigned account ownership of the contact

        unsubscribed_from_emails : typing.Optional[bool]
            Whether the contact is unsubscribed from emails

        custom_attributes : typing.Optional[typing.Dict[str, typing.Any]]
            The custom attributes which are set for the contact

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ContactsUpdateResponse]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}",
            method="PUT",
            json={
                "role": role,
                "external_id": external_id,
                "email": email,
                "phone": phone,
                "name": name,
                "avatar": avatar,
                "signed_up_at": signed_up_at,
                "last_seen_at": last_seen_at,
                "owner_id": owner_id,
                "unsubscribed_from_emails": unsubscribed_from_emails,
                "custom_attributes": custom_attributes,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactsUpdateResponse,
                    construct_type(
                        type_=ContactsUpdateResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def delete(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ContactDeleted]:
        """
        You can delete a single contact.

        Parameters
        ----------
        contact_id : str
            contact_id

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ContactDeleted]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactDeleted,
                    construct_type(
                        type_=ContactDeleted,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def merge_lead_in_user(
        self,
        *,
        lead_id: typing.Optional[str] = OMIT,
        contact_id: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[ContactsMergeLeadInUserResponse]:
        """
        You can merge a contact with a `role` of `lead` into a contact with a `role` of `user`.

        Parameters
        ----------
        lead_id : typing.Optional[str]
            The unique identifier for the contact to merge away from. Must be a lead.

        contact_id : typing.Optional[str]
            The unique identifier for the contact to merge into. Must be a user.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ContactsMergeLeadInUserResponse]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            "contacts/merge",
            method="POST",
            json={
                "from": lead_id,
                "into": contact_id,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactsMergeLeadInUserResponse,
                    construct_type(
                        type_=ContactsMergeLeadInUserResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def search(
        self,
        *,
        query: SearchRequestQuery,
        pagination: typing.Optional[StartingAfterPaging] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncPager[Contact, ContactList]:
        """
        You can search for multiple contacts by the value of their attributes in order to fetch exactly who you want.

        To search for contacts, you need to send a `POST` request to `https://api.intercom.io/contacts/search`.

        This will accept a query object in the body which will define your filters in order to search for contacts.

        {% admonition type="warning" name="Optimizing search queries" %}
          Search queries can be complex, so optimizing them can help the performance of your search.
          Use the `AND` and `OR` operators to combine multiple filters to get the exact results you need and utilize
          pagination to limit the number of results returned. The default is `50` results per page.
          See the [pagination section](https://developers.intercom.com/docs/build-an-integration/learn-more/rest-apis/pagination/#example-search-conversations-request) for more details on how to use the `starting_after` param.
        {% /admonition %}
        ### Contact Creation Delay

        If a contact has recently been created, there is a possibility that it will not yet be available when searching. This means that it may not appear in the response. This delay can take a few minutes. If you need to be instantly notified it is recommended to use webhooks and iterate to see if they match your search filters.

        ### Nesting & Limitations

        You can nest these filters in order to get even more granular insights that pinpoint exactly what you need. Example: (1 OR 2) AND (3 OR 4).
        There are some limitations to the amount of multiple's there can be:
        * There's a limit of max 2 nested filters
        * There's a limit of max 15 filters for each AND or OR group

        ### Searching for Timestamp Fields

        All timestamp fields (created_at, updated_at etc.) are indexed as Dates for Contact Search queries; Datetime queries are not currently supported. This means you can only query for timestamp fields by day - not hour, minute or second.
        For example, if you search for all Contacts with a created_at value greater (>) than 1577869200 (the UNIX timestamp for January 1st, 2020 9:00 AM), that will be interpreted as 1577836800 (January 1st, 2020 12:00 AM). The search results will then include Contacts created from January 2nd, 2020 12:00 AM onwards.
        If you'd like to get contacts created on January 1st, 2020 you should search with a created_at value equal (=) to 1577836800 (January 1st, 2020 12:00 AM).
        This behaviour applies only to timestamps used in search queries. The search results will still contain the full UNIX timestamp and be sorted accordingly.

        ### Accepted Fields

        Most key listed as part of the Contacts Model are searchable, whether writeable or not. The value you search for has to match the accepted type, otherwise the query will fail (ie. as `created_at` accepts a date, the `value` cannot be a string such as `"foorbar"`).

        | Field                              | Type                           |
        | ---------------------------------- | ------------------------------ |
        | id                                 | String                         |
        | role                               | String<br>Accepts user or lead |
        | name                               | String                         |
        | avatar                             | String                         |
        | owner_id                           | Integer                        |
        | email                              | String                         |
        | email_domain                       | String                         |
        | phone                              | String                         |
        | external_id                        | String                         |
        | created_at                         | Date (UNIX Timestamp)          |
        | signed_up_at                       | Date (UNIX Timestamp)          |
        | updated_at                         | Date (UNIX Timestamp)          |
        | last_seen_at                       | Date (UNIX Timestamp)          |
        | last_contacted_at                  | Date (UNIX Timestamp)          |
        | last_replied_at                    | Date (UNIX Timestamp)          |
        | last_email_opened_at               | Date (UNIX Timestamp)          |
        | last_email_clicked_at              | Date (UNIX Timestamp)          |
        | language_override                  | String                         |
        | browser                            | String                         |
        | browser_language                   | String                         |
        | os                                 | String                         |
        | location.country                   | String                         |
        | location.region                    | String                         |
        | location.city                      | String                         |
        | unsubscribed_from_emails           | Boolean                        |
        | marked_email_as_spam               | Boolean                        |
        | has_hard_bounced                   | Boolean                        |
        | ios_last_seen_at                   | Date (UNIX Timestamp)          |
        | ios_app_version                    | String                         |
        | ios_device                         | String                         |
        | ios_app_device                     | String                         |
        | ios_os_version                     | String                         |
        | ios_app_name                       | String                         |
        | ios_sdk_version                    | String                         |
        | android_last_seen_at               | Date (UNIX Timestamp)          |
        | android_app_version                | String                         |
        | android_device                     | String                         |
        | android_app_name                   | String                         |
        | andoid_sdk_version                 | String                         |
        | segment_id                         | String                         |
        | tag_id                             | String                         |
        | custom_attributes.{attribute_name} | String                         |

        ### Accepted Operators

        {% admonition type="warning" name="Searching based on `created_at`" %}
          You cannot use the `<=` or `>=` operators to search by `created_at`.
        {% /admonition %}

        The table below shows the operators you can use to define how you want to search for the value.  The operator should be put in as a string (`"="`). The operator has to be compatible with the field's type (eg. you cannot search with `>` for a given string value as it's only compatible for integer's and dates).

        | Operator | Valid Types                      | Description                                                      |
        | :------- | :------------------------------- | :--------------------------------------------------------------- |
        | =        | All                              | Equals                                                           |
        | !=       | All                              | Doesn't Equal                                                    |
        | IN       | All                              | In<br>Shortcut for `OR` queries<br>Values must be in Array       |
        | NIN      | All                              | Not In<br>Shortcut for `OR !` queries<br>Values must be in Array |
        | >        | Integer<br>Date (UNIX Timestamp) | Greater than                                                     |
        | <       | Integer<br>Date (UNIX Timestamp) | Lower than                                                       |
        | ~        | String                           | Contains                                                         |
        | !~       | String                           | Doesn't Contain                                                  |
        | ^        | String                           | Starts With                                                      |
        | $        | String                           | Ends With                                                        |

        Parameters
        ----------
        query : SearchRequestQuery

        pagination : typing.Optional[StartingAfterPaging]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncPager[Contact, ContactList]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            "contacts/search",
            method="POST",
            json={
                "query": convert_and_respect_annotation_metadata(
                    object_=query, annotation=SearchRequestQuery, direction="write"
                ),
                "pagination": convert_and_respect_annotation_metadata(
                    object_=pagination, annotation=StartingAfterPaging, direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ContactList,
                    construct_type(
                        type_=ContactList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _items = _parsed_response.data
                _has_next = False
                _get_next = None
                if _parsed_response.pages is not None and _parsed_response.pages.next is not None:
                    _parsed_next = _parsed_response.pages.next.starting_after
                    _has_next = _parsed_next is not None and _parsed_next != ""

                    async def _get_next():
                        return await self.search(
                            query=query,
                            pagination=pagination,
                            request_options=request_options,
                        )

                return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def list(
        self,
        *,
        page: typing.Optional[int] = None,
        per_page: typing.Optional[int] = None,
        starting_after: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncPager[Contact, ContactList]:
        """
        You can fetch a list of all contacts (ie. users or leads) in your workspace.
        {% admonition type="warning" name="Pagination" %}
          You can use pagination to limit the number of results returned. The default is `50` results per page.
          See the [pagination section](https://developers.intercom.com/docs/build-an-integration/learn-more/rest-apis/pagination/#pagination-for-list-apis) for more details on how to use the `starting_after` param.
        {% /admonition %}

        Parameters
        ----------
        page : typing.Optional[int]
            The page of results to fetch. Defaults to first page

        per_page : typing.Optional[int]
            How many results to display per page. Defaults to 15

        starting_after : typing.Optional[str]
            String used to get the next page of conversations.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncPager[Contact, ContactList]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            "contacts",
            method="GET",
            params={
                "page": page,
                "per_page": per_page,
                "starting_after": starting_after,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ContactList,
                    construct_type(
                        type_=ContactList,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _items = _parsed_response.data
                _has_next = False
                _get_next = None
                if _parsed_response.pages is not None and _parsed_response.pages.next is not None:
                    _parsed_next = _parsed_response.pages.next.starting_after
                    _has_next = _parsed_next is not None and _parsed_next != ""

                    async def _get_next():
                        return await self.list(
                            page=page,
                            per_page=per_page,
                            starting_after=_parsed_next,
                            request_options=request_options,
                        )

                return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def create(
        self, *, request: CreateContactRequest, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ContactsCreateResponse]:
        """
        You can create a new contact (ie. user or lead).

        Parameters
        ----------
        request : CreateContactRequest

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ContactsCreateResponse]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            "contacts",
            method="POST",
            json=convert_and_respect_annotation_metadata(
                object_=request, annotation=CreateContactRequest, direction="write"
            ),
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactsCreateResponse,
                    construct_type(
                        type_=ContactsCreateResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def show_contact_by_external_id(
        self, external_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ShowContactByExternalIdResponse]:
        """
        You can fetch the details of a single contact by external ID. Note that this endpoint only supports users and not leads.

        Parameters
        ----------
        external_id : str
            The external ID of the user that you want to retrieve

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ShowContactByExternalIdResponse]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/find_by_external_id/{jsonable_encoder(external_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ShowContactByExternalIdResponse,
                    construct_type(
                        type_=ShowContactByExternalIdResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 401:
                raise UnauthorizedError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        construct_type(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def archive(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ContactArchived]:
        """
        You can archive a single contact.

        Parameters
        ----------
        contact_id : str
            contact_id

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ContactArchived]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/archive",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactArchived,
                    construct_type(
                        type_=ContactArchived,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def unarchive(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ContactUnarchived]:
        """
        You can unarchive a single contact.

        Parameters
        ----------
        contact_id : str
            contact_id

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ContactUnarchived]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/unarchive",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactUnarchived,
                    construct_type(
                        type_=ContactUnarchived,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def block_contact(
        self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> AsyncHttpResponse[ContactBlocked]:
        """
        Block a single contact.<br>**Note:** conversations of the contact will also be archived during the process.<br>More details in [FAQ How do I block Inbox spam?](https://www.intercom.com/help/en/articles/8838656-inbox-faqs)

        Parameters
        ----------
        contact_id : str
            contact_id

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[ContactBlocked]
            successful
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"contacts/{jsonable_encoder(contact_id)}/block",
            method="POST",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    ContactBlocked,
                    construct_type(
                        type_=ContactBlocked,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
